प्रौद्योगिकी साझेदारी

Go coroutines and channels इत्यस्य व्यापकाः अनुप्रयोगविषयाः

2024-07-12

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

1. संक्षेपेण अवगच्छन्तु यत् कोर्युटिन्स्, चैनल्स् च किम्

कोरोटीन इति किम्

कोरौटिन् एकः उपयोक्तृस्तरीयः लघुसूत्रः अस्ति यस्य स्वतन्त्रः स्टैक् स्पेसः भवति तथा च प्रोग्रामस्य ढेरस्थानं साझां करोति ।

एतत् एकसूत्रस्य आधारेण एल्गोरिदम्-माध्यमेन कार्यान्वितः सूक्ष्म-सूत्रम् अस्ति बहु-सूत्र-प्रोग्रामिङ्गस्य तुलने अस्य निम्नलिखित-लाभाः सन्ति ।

  • कोरौटिन् इत्यस्य सन्दर्भस्विचिंग् उपयोक्त्रा निर्धारितं भवति, सिस्टम् कर्नेल् इत्यस्य सन्दर्भस्विचिंग् इत्यस्य आवश्यकतां विना, ओवरहेड् न्यूनीकरोति ।
  • पूर्वनिर्धारितरूपेण, व्यत्ययनिवारणाय कोरौटिन् पूर्णतया सुरक्षिताः भवन्ति ।परमाणुसञ्चालनतालस्य आवश्यकता नास्ति
  • एकः सूत्रः उच्चसमवर्ततां अपि प्राप्तुं शक्नोति, एककोर-सीपीयू अपि दशसहस्राणि कोर्युटिन्-समर्थनं कर्तुं शक्नोति ।

चैनल इति किम्

चैनल् इति एकः दत्तांशसंरचना यस्य उपयोगः कोर्युटिन्-मध्ये संचारार्थं भवति । पङ्क्तिसदृशम् एकः अन्तः प्रेषकः अपरः अन्तः ग्राहकः । चैनल्-उपयोगेन दत्तांशस्य समन्वयनं क्रमं च सुनिश्चितं कर्तुं शक्यते ।

चैनल्स् बफरयुक्तेषु चैनलेषु, अबफरयुक्तेषु चॅनेलेषु च विभक्ताः भवन्ति, ये निम्नलिखितरूपेण घोषिताः भवन्ति ।

  • तत्र बफर-चैनलः अस्ति
intChan := make(chan int,<缓冲容量>)
  • अनबफर चैनल
intChan := make(chan int)

बफर-चैनल-अनबफर-चैनेल्-योः मध्ये अन्तरम् :

  • अवरोधनम्: अबफर-चैनलस्य प्रेषकः यावत् दत्तांशः न प्राप्यते तावत् अवरुद्धं करिष्यति, यावत् बफरः पूर्णः न भवति तावत् ग्राहकः अवरुद्धं करिष्यति;
  • आँकडा समन्वयनं अनुक्रमं च : अबफरयुक्ताः चैनलाः आँकडा समन्वयनस्य अनुक्रमस्य च गारण्टीं ददति;
  • अनुप्रयोग परिदृश्यानि: अबफर-चैनलेषु सख्त-समन्वयनस्य आवश्यकता भवति तथा च बफर-चैनेल्-अतुल्यकालिकरूपेण संवादं कर्तुं शक्नुवन्ति तथा च थ्रूपुट्-सुधारं कर्तुं शक्नुवन्ति;

अबफर-चैनेल्-कार्यन्वयने यत् ज्ञातव्यं तत् अस्ति यत् चैनल्-उभय-अन्तयोः प्रेषकः ग्राहकश्च भवितुमर्हति, अन्यथा गतिरोधः भविष्यति

2.Coroutine-चैनल समवर्ती प्रोग्रामिंग प्रकरण

(1) अक्षराणि संख्याश्च क्रमेण मुद्रयन्तु

प्रश्नस्य अर्थः : क्रमेण 1-10 संख्याः AJ अक्षराणि च मुद्रयितुं coroutine-channel इत्यस्य उपयोगं कुर्वन्तु।

संहिता : १.

  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. }

विषयविश्लेषणम् : १.

प्रश्ने अस्माभिः क्रमेण अक्षराणां सङ्ख्यानां च मुद्रणं करणीयम्, अतः अस्माभिः द्वयोः कोर्युटिन्योः कठोरक्रमः सुनिश्चितः करणीयः, यः अबफर-चैनलस्य अनुप्रयोग-परिदृश्येन सह सङ्गतः अस्ति क्रमशः सङ्ख्याः अक्षराणि च संग्रहीतुं द्वौ चॅनेलौ स्थापयन्तु ये क्रमेण सङ्ख्याः अक्षराणि च मुद्रयन्ति। एकवारं लूप् मध्ये मुद्रयन्तु तथा च एकवारं संकेतं प्रेषयन्तु यत् अन्यं कोर्युटिन् मुद्रयितुं स्मरणं कुर्वन्तु ।

ज्ञातव्यं यत् यदा अन्तिमः वर्णः '10' मुद्रितः भवति तदा अक्षराणां मुद्रणार्थं कोरौटिन् समाप्तं भवति, तथा च numCh चैनल् इत्यस्य ग्राहकः नास्ति, अस्मिन् समये, अबफर-चैनलस्य कार्यान्वयन-शर्ताः न पूर्यन्ते - अवश्यमेव सन्ति प्रेषकः ग्राहकश्च अन्यथा पुनः संकेतं प्रेषयित्वा अवरोधन गतिरोधः भविष्यति । अतः पुनः १० वारं संकेतं प्रेषयितुं आवश्यकता नास्ति।

(2) कार्यनिर्मापकस्य डिजाइनं कुर्वन्तु

शीर्षकम्: एकं कार्यनिर्मातारं डिजाइनं कुर्वन्तु यत् बहु-कार्यस्य समवर्ती-प्रक्रियाकरणस्य व्यावसायिक-परिदृश्यानां कार्यान्वयनार्थं बहु-कोरोटीन + चैनल-प्रोग्रामिंग-प्रतिरूपस्य उपयोगं करोति, तथा च समयनिर्धारण-क्रमस्य आवश्यकता यस्मिन् क्रमे कार्याणि योजिताः भवन्ति तस्मिन् क्रमे भवितुं आवश्यकम्

संहिता : १.

  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. }

समस्याविश्लेषणम् : १.

यतो हि योजितकार्यं बहुकार्यं भवति, तस्मात् एकादशाधिकं भवति, एतानि कार्याणि निष्पादयितुं अतुल्यकालिकप्रक्रिया आवश्यकी भवति । बफर-चैनलस्य अनुपालनाय उन्नत-थ्रूपुट्-अतुल्यकालिक-प्रक्रियाकरणस्य आवश्यकता भवति ।

ततः, अस्माभिः कार्यं चैनल् मध्ये स्थापयितुं आवश्यकम्, तथा च बहुविधाः ग्राहकाः चैनल् तः कार्याणि क्रमेण गृहीत्वा निष्पादयितुं शक्नुवन्ति ।

समस्या यस्याः ध्यानस्य आवश्यकता वर्तते सा अस्ति यत् यदि योजितानां कार्याणां संख्या चैनलस्य बफरात् अधिका भवति तर्हि कार्याणि योजयित्वा अवरोधं जनयिष्यति । उपभोक्तुः सामान्यप्रारम्भं न प्रभावितं कर्तुं कार्याणि योजयितुं पृथक् कोरूटिन् उद्घाटयितुं आवश्यकम् ।

एवं प्रकारेण यदा उपभोक्ता उपभोगं करोति तदा अवरोधकः उत्पादकः कार्याणि निरन्तरं योजयितुं जागरितः भविष्यति ।

3. सारांशः

coroutine + channel programming model इत्यस्य अध्ययनानन्तरं शीर्षके एव यत् उक्तं तस्य अतिरिक्तं अस्माभिः निम्नलिखितविषयेषु अपि ध्यानं दातव्यम् ।

1. चैनलस्य उपयोगः कृतः ततः परं किमर्थं बन्दं कर्तव्यम् ?

  • गतिरोधं परिहरितुं । चैनलस्य बन्दीकरणेन ग्राहकः अपि ज्ञायते यत् प्रेषकात् प्रेषयितुं अधिकं दत्तांशः नास्ति तथा च दत्तांशस्य प्रतीक्षां निरन्तरं कर्तुं आवश्यकता नास्ति । चैनलस्य समापनसूचनायाः प्राप्तेः अनन्तरं ग्राहकः दत्तांशं प्राप्तुं स्थगयति यदि चैनलः न बन्दः भवति तर्हि ग्राहकः अवरुद्धः एव तिष्ठति तथा च गतिरोधस्य जोखिमः भवति
  • संसाधनं मुक्तं कुर्वन्तु संसाधनस्य लीकं च परिहरन्तु। चैनलं बन्दं कृत्वा प्रणाली तत्सम्बद्धानि संसाधनानि मुक्तं करिष्यति ।

2. चैनलं कथं ललिततया बन्दं कर्तव्यम् ?

प्रथमं तु मार्गस्य बन्दीकरणस्य मूलभूततमः सिद्धान्तः अस्ति यत् ये मार्गाः बन्दाः सन्ति तेषां बन्दीकरणं न करणीयम् । द्वितीयं, Go चैनल् इत्यस्य उपयोगाय अन्यः सिद्धान्तः अस्ति :दत्तांशग्राहके वा यदा बहुविधप्रेषकाः सन्ति तदा वा चैनलं न पिधायन्तु।अन्येषु शब्देषुअस्माभिः केवलं कस्यचित् चैनलस्य एकमात्रं प्रेषकं एतत् चैनलं बन्दं कर्तुं अर्हति।

एकः अशिष्टः उपायः अपवादपुनर्प्राप्तिद्वारा चैनलं बन्दं कर्तुं भवति, परन्तु एतत् स्पष्टतया उपर्युक्तसिद्धान्तानां उल्लङ्घनं करोति तथा च अन्यः उपायः अस्ति यत् sync.Once अथवा sync.Mutex इत्यनेन चैनलं बन्दं करणीयम्, यत् Concurrent close इति भवितुं गारण्टी नास्ति एकस्मिन् चैनले ऑपरेशन्स् तथा सेण्ड् ऑपरेशन्स् च दत्तांशदौडं न निर्मान्ति । उभयोः पद्धतयोः कतिपयानि समस्यानि सन्ति, अतः अहं तान् विस्तरेण न परिचययिष्यामि ।

परिदृश्यम् १ : M ग्राहकाः एकः प्रेषकः च

निवारणार्थं सुलभतमासु परिस्थितिषु अन्यतमा। यदा प्रेषकस्य प्रेषणं समाप्तं कर्तव्यं भवति तदा केवलं तत् चैनलं पिधातु । उपरिष्टात् प्रोग्रामिंग् उदाहरणद्वये एतत् ।

परिदृश्यम् २ : एकः ग्राहकः एन प्रेषकः च

Go channels इत्यस्य मूलभूतसिद्धान्तानुसारं वयं केवलं चैनलस्य एकमात्रप्रेषकस्थाने एव चैनलं पिधातुं शक्नुमः । अतः, अस्मिन् सन्दर्भे वयं प्रत्यक्षतया कुत्रचित् चैनलं पिधातुं न शक्नुमः ।परन्तु वयं ग्राहकं अतिरिक्तं संकेतमार्गं बन्दं कर्तुं शक्नुमः यत् प्रेषकं अधिकं दत्तांशं न प्रेषयितुं वक्तुं शक्नोति।

  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. }

अस्मिन् पद्धत्या वयं अतिरिक्तं सिग्नल् चैनल् stopCh योजयामः, यस्य उपयोगेन ग्राहकः प्रेषकं कथयति यत् तस्य दत्तांशस्य प्राप्तेः आवश्यकता नास्ति इति । अपि च, एषा पद्धतिः dataCh न बन्दं करोति यदा कश्चन चैनलः कस्यापि कोरटीनेन न पुनः उपयुज्यते तदा सः क्रमेण कचरासंगृहीतः भविष्यति, यद्यपि सः बन्दः अस्ति वा।

अस्य पद्धतेः सौन्दर्यं अस्ति यत् एकं चैनलं पिधाय अन्यस्य चैनलस्य उपयोगं त्यजति, तस्मात् अन्यं चैनलं परोक्षरूपेण पिधायति ।

परिदृश्यम् ३ : M ग्राहकाः N प्रेषकाः च

वयं ग्राहकं प्रेषकं वा दत्तांशसञ्चारार्थं प्रयुक्तं मार्गं बन्दं कर्तुं न शक्नुमः, न च बहुग्राहिषु एकं अतिरिक्तं संकेतमार्गं बन्दं कर्तुं शक्नुमः एतयोः द्वयोः अपि अभ्यासयोः चैनल् बन्दीकरणसिद्धान्तस्य उल्लङ्घनं भवति ।

तथापि वयं परिचयं कर्तुं शक्नुमःएकः मध्यवर्ती मध्यस्थभूमिका तथा च कार्यस्य समाप्तेः सर्वेभ्यः ग्राहकेभ्यः प्रेषकेभ्यः च सूचयितुं अतिरिक्तसंकेतमार्गान् बन्दं कृत्वा

कोड उदाहरणम् : १.

  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. }