Technology sharing

Comprehensiva applicationis quaestiones de Coroutines et canales Ite

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. Breviter intelligere quid coroutines et canales sunt

Quid est coroutine

Coroutinum est filum leve-gradu usoris quod spatium acervum sui iuris habet et spatium cumulum programmatis communicat.

Micro-fila per algorithms in ex uno sequela comparata est, cum multi-filam programmandi comparata, sequentia commoda habet:

  • Contextus permutatio coroutini ab utentis decernitur, sine necessitate contextus mutandi nuclei systematis, caput reducendi.
  • Defaltam, coroutines ne interruptiones plene muniantur.Nulla nuclei operatio cincinno requiratur
  • Unum linum etiam concursum altum consequi potest, et etiam unum nucleum CPU decem milia coroutinum sustinere potest.

quid channel?

Channel data est structura ad communicationem inter coroutines adhibita. A queue similis, unus finis est mittenti et alter finis receptor. Usura canales efficere potest synchronisationem et ordinem notitiae.

Canales dividuntur in canales buffered et canales sineferos, qui hoc modo declarantur:

  • Est quiddam channel
intChan := make(chan int,<缓冲容量>)
  • unuffered channel
intChan := make(chan int)

Discrimen inter canales buffered et canales non effertos;

  • Clausus: Missor alvei soluti obstruet donec notitia recipiatur; Mittens canalem buffered obstruet usque dum quiddam plenum est, et acceptor obstruet donec quiddam vacuum non sit.
  • Data synchronisation and sequence: canales non buffered synchronisation and sequence data spondent;
  • Applicationes missiones: canales sine offensis strictam synchronizationem et seriem requirunt;

Quod notandum est in exsequendis canalibus intemperatis est mittente et receptore ad utrumque alveum esse oportere, alioquin morticinum erit.

2. Coroutine-alveum concurrentem programmandi causa

(I) Print litterae et numeri alternatim

Quaeritur significatio: Utere canali coroutino ad imprimendos numeros alternatim 1-10 et litteras AJ.

Codicis:

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. /*
  7. 无缓冲chanel:需要在写入chanel的时候要保证有另外一个协程在读取chanel。否则会导致写端阻塞,发生死锁
  8. 解决办法:
  9. 避免死锁的发生:
  10. 当i循环到10时,printAlp协程已然结束,所以此时不必再写入alp通道
  11. */
  12. func printNum(wg *sync.WaitGroup, numCh chan struct{}, alpCh chan struct{}) {
  13. defer wg.Done()
  14. for i := 1; i <= 10; i++ {
  15. <-alpCh // 等待字母goroutine发信号
  16. fmt.Print(i, " ")
  17. //避免死锁发生
  18. if i < 10 {
  19. numCh <- struct{}{} // 发信号给字母goroutine
  20. }
  21. if i == 10 {
  22. close(numCh)
  23. }
  24. }
  25. }
  26. func printAlp(wg *sync.WaitGroup, numCh chan struct{}, alpCh chan struct{}) {
  27. defer wg.Done()
  28. for i := 'A'; i <= 'J'; i++ {
  29. <-numCh // 等待数字goroutine发信号
  30. fmt.Printf("%c", i)
  31. alpCh <- struct{}{} // 发信号给数字goroutine
  32. }
  33. close(alpCh)
  34. }
  35. func main() {
  36. numCh := make(chan struct{}) // 用于数字goroutine的信号通道
  37. alpCh := make(chan struct{}) // 用于字母goroutine的信号通道
  38. var wg sync.WaitGroup
  39. wg.Add(2)
  40. go printAlp(&wg, numCh, alpCh)
  41. go printNum(&wg, numCh, alpCh)
  42. // 启动时先给数字goroutine发送一个信号
  43. numCh <- struct{}{}
  44. wg.Wait()
  45. }

Analysis topic:

Interrogatio nos postulat ut epistolas et numeros alternatim imprimant, ut necesse sit ut ordo districtus duorum coroutinorum, qui applicatione canalium non effusorum missione congruat. Duos canales constitue ut numeros et litteras respective condant. Imprime semel in ansa et signum mittat semel ut alium coroutinum imprimat admoneat.

Animadvertendum est, cum ultima characteris "10" impressus est, coroutinum litterarum imprimendi finivit, et numCh alveus non habet receptorem. Hoc tempore, condiciones exsecutionis canalis gratis non amplius convenerunt - oportet esse Mittens et Receptaculum. Alioquin mittens signum iterum claudendi causa deadlock. Itaque non est necesse ut signum iterum 10 tempore mittat.

(II) Designare negotium scheduler

Title: Designa munus scheduler quod utitur exemplo programmandi multi-coroutine + canalis ad negotium deducendi missiones concurrentis processus multi- munerum, et requirit schedulingum ordinem quo adduntur opera.

Codicis:

  1. type scheduler struct {
  2. taskChan chan func()
  3. wt sync.WaitGroup
  4. }
  5. func (td *scheduler) AddTask(task func()) {
  6. td.taskChan <- task
  7. }
  8. func (td *scheduler) Executer() {
  9. defer td.wt.Done()
  10. for {
  11. task, ok := <-td.taskChan
  12. task()
  13. if ok && len(td.taskChan) == 0 {
  14. break
  15. }
  16. }
  17. }
  18. func (td *scheduler) Start() {
  19. td.wt.Add(4)
  20. //假设四个消费者
  21. for i := 0; i < 4; i++ {
  22. go td.Executer()
  23. }
  24. td.wt.Wait()
  25. }
  26. func main() {
  27. sd := scheduler{
  28. taskChan: make(chan func(), 5),
  29. }
  30. go func() {
  31. sd.AddTask(func() {
  32. fmt.Println("任务1")
  33. })
  34. sd.AddTask(func() {
  35. fmt.Println("任务2")
  36. })
  37. sd.AddTask(func() {
  38. fmt.Println("任务3")
  39. })
  40. sd.AddTask(func() {
  41. fmt.Println("任务4")
  42. })
  43. sd.AddTask(func() {
  44. fmt.Println("任务5")
  45. })
  46. sd.AddTask(func() {
  47. fmt.Println("任务6")
  48. })
  49. close(sd.taskChan)
  50. }()
  51. sd.Start()
  52. }

quaestio analysis:

Cum multae functiones additae sint, plus quam unum est, et processus asynchronus ad haec munera exercenda requiritur. Obsequor canalibus buffered emendatis throughput et asynchronous processui requirit.

Tum opus in canalem imponere oportet, multique accipientes munus ab alveo accipere possunt ut ea exequantur et exsequantur.

Problema, quod attentione indiget, est quod, si numerus operum additorum maior est quam quiddam canalis, obturationem faciet cum muneribus additis. Ut normalem satus of perussi non afficit, coroutine separatum aperire debet opera addere.

Hoc modo, cum consumit edax, effectrix interclusio excitabitur ad operas addendo.

3. Libri

Postquam exemplar programmationis coroutine + canalis pervestigamus, praeter ea quae in titulo proxime nominata sunt, etiam ad quaestiones sequentes attendendum debemus:

1. Cur alveus claudendus est postquam consumitur?

  • Ad vitare deadlock. Canalem claudunt etiam receptatorem narrat nulla plus data esse ut mittat a mittente et non opus est ut notitias exspectando pergas. Recepta canalis claudendi informationes, receptaculum sistit notitia accepta; si canalis non clauditur, acceptor clausus remanebit et periculum deadlock est.
  • Dimitte opes et fuge resource pinum. Post canalem claudendum, systema debitas facultates solvet.

2. Quomodo canalem lepide claudere?

Ante omnia, principium principalissimum canalium claudendi non est claudere canales clausis. Secundo est aliud principium utendi canales Go;Noli claudere canalem in notitia accipientis vel cum plures sunt mittentes.in aliis verbisTantum canalem mittentem solum hunc canalem claudere debemus.

Via rudis est claudere canalem per receptam exceptionem, sed manifesto illa principia violat, et genus notitiarum causare potest; alius modus est sync. Mutex claudere canalem operationes et operationes in canalem mittere non faciunt data gentes. Utriusque modi problemata quaedam habent, ideo eas singillatim non introducebo. Hic modus est quomodo canalem lepide claudat.

Sem I: M receptores et mittentis

Una ex facillimis adiunctis euenit. Cum mittens necesse est ut mittendo perficiat, canalem mox claudat. Ita est in duobus programmatis exemplis.

Sem II: unus receptor et N mittentes

Secundum principia fundamentalia Canalium Go, solum possumus canalem claudere ad unicum mittente canalis. Ita, hoc casu, alveum alicubi directe claudere non possumus.Sed accipientem prope canalem additicium signum permittere possumus ut mittente non amplius notitias mitteret.

  1. package main
  2. import (
  3. "log"
  4. "sync"
  5. )
  6. func main() {
  7. cosnt N := 5
  8. cosnt Max := 60000
  9. count := 0
  10. dataCh := make(chan int)
  11. stopCh := make(chan bool)
  12. var wt sync.WaitGroup
  13. wt.Add(1)
  14. //发送者
  15. for i := 0; i < N; i++ {
  16. go func() {
  17. for {
  18. select {
  19. case <-stopCh:
  20. return
  21. default:
  22. count += 1
  23. dataCh <- count
  24. }
  25. }
  26. }()
  27. }
  28. //接收者
  29. go func() {
  30. defer wt.Done()
  31. for value := range dataCh {
  32. if value == Max {
  33. // 此唯一的接收者同时也是stopCh通道的
  34. // 唯一发送者。尽管它不能安全地关闭dataCh数
  35. // 据通道,但它可以安全地关闭stopCh通道。
  36. close(stopCh)
  37. return
  38. }
  39. log.Println(value)
  40. }
  41. }()
  42. wt.Wait()
  43. }

Hoc pacto addito canali stopCh signo addito, quo receptor utitur ad mittente indicandam notitias recipere non amplius oportere. Haec autem methodus dataCh non claudit. Cum canalis coroutine nulla amplius adhibetur, paulatim coeno colligitur, quantumvis occlusum est.

Elegantia huius methodi est ut unum alveum claudendo, alio canali uti desinas, ita alterum alveum oblique claudendo.

Sem III, M accipientium et N mittentium

Accipientem vel mittentem prope canalem habere non possumus, qui notitias tradendas adhibebat, nec unum ex multis receptoribus habere possumus, accessionem canalem significationis prope. Utrumque horum usuum principium clausurae canalis violant.

Sed inducere possumusIntermedium munus intermedium et additos canales significationis claudat ut omnes receptores et mittentes finis operis certiorem faciat.

Exemplum codicis:

  1. package main
  2. import (
  3. "log"
  4. "math/rand"
  5. "strconv"
  6. "sync"
  7. )
  8. func main() {
  9. const Max = 100000
  10. const NumReceivers = 10
  11. const NumSenders = 1000
  12. var wt sync.WaitGroup
  13. wt.Add(NumReceivers)
  14. dataCh := make(chan int)
  15. stopCh := make(chan struct{})
  16. // stopCh是一个额外的信号通道。它的发送
  17. // 者为中间调解者。它的接收者为dataCh
  18. // 数据通道的所有的发送者和接收者。
  19. toStop := make(chan string, 1)
  20. // toStop是一个用来通知中间调解者让其
  21. // 关闭信号通道stopCh的第二个信号通道。
  22. // 此第二个信号通道的发送者为dataCh数据
  23. // 通道的所有的发送者和接收者,它的接收者
  24. // 为中间调解者。它必须为一个缓冲通道。
  25. var stoppedBy string
  26. // 中间调解者
  27. go func() {
  28. stoppedBy = <-toStop
  29. close(stopCh)
  30. }()
  31. // 发送者
  32. for i := 0; i < NumSenders; i++ {
  33. go func(id string) {
  34. for {
  35. value := rand.Intn(Max)
  36. if value == 0 {
  37. // 为了防止阻塞,这里使用了一个尝试
  38. // 发送操作来向中间调解者发送信号。
  39. select {
  40. case toStop <- "发送者#" + id:
  41. default:
  42. }
  43. return
  44. }
  45. select {
  46. case <-stopCh:
  47. return
  48. case dataCh <- value:
  49. }
  50. }
  51. }(strconv.Itoa(i))
  52. }
  53. // 接收者
  54. for i := 0; i < NumReceivers; i++ {
  55. go func(id string) {
  56. defer wt.Done()
  57. for {
  58. select {
  59. case <-stopCh:
  60. return
  61. case value := <-dataCh:
  62. if value == Max {
  63. // 为了防止阻塞,这里使用了一个尝试
  64. // 发送操作来向中间调解者发送信号。
  65. select {
  66. case toStop <- "接收者:" + id:
  67. default:
  68. }
  69. return
  70. }
  71. log.Println(value)
  72. }
  73. }
  74. }(strconv.Itoa(i))
  75. }
  76. wt.Wait()
  77. log.Println("被" + stoppedBy + "终止了")
  78. }