为什么在这个功能中使用通道



我正在研究一个关于使用go例程的时间安排的博客,我看到下面粘贴的示例,从第61行到第65行。但我不明白在这里使用频道的目的。

他似乎在重复通道以检索go例程中的消息。但是为什么不直接使用字符串数组呢?

58 func findConcurrent(goroutines int, topic string, docs []string) int {
59     var found int64
60
61     ch := make(chan string, len(docs))
62     for _, doc := range docs {
63         ch <- doc
64     }
65     close(ch)
66
67     var wg sync.WaitGroup
68     wg.Add(goroutines)
69
70     for g := 0; g < goroutines; g++ {
71         go func() {
72             var lFound int64
73             for doc := range ch {
74                 items, err := read(doc)
75                 if err != nil {
76                     continue
77                 }
78                 for _, item := range items {
79                     if strings.Contains(item.Description, topic) {
80                         lFound++
81                     }
82                 }
83             }
84             atomic.AddInt64(&found, lFound)
85             wg.Done()
86         }()
87     }
88
89     wg.Wait()
90
91     return int(found)
92 }

此代码提供了一个在多个goRoutine之间分配工作(在文档中查找字符串(的方法示例。基本上,代码是启动goroutines并通过通道向他们提供要搜索的文档。

但是为什么不直接使用字符串数组呢?

可以使用字符串数组和变量(让我们称之为count(来跟踪数组中的项目

for {
if count > len(docarray) {
break;
}
doc := docarray[count]
count++
// Process the document
}

然而,您会遇到同步问题。例如,如果两个go例程(运行在不同的处理器内核上(同时到达if count > len(docarray),会发生什么?如果没有防止这种情况发生的措施,它们可能最终都会处理切片中的同一项(并可能跳过下一个元素,因为它们都运行count++(。

流程的同步化非常复杂,而且问题很难调试。使用通道对您隐藏了很多这种复杂性,并使您的代码更有可能按预期工作(它并不能解决所有问题;请注意,在示例代码中使用atomic.AddInt64(&found, lFound)可以防止另一个潜在问题,该问题可能由多个go例程同时写入一个变量引起(。

作者似乎只是在使用一个人为的例子来说明通道是如何工作的。也许他应该拿出一个更现实的例子。但他确实说:

注意:在编写add的并发版本时,可以采用几种方法和选项。此时不要对我的特定实现感到困惑。如果你有一个可读性更强的版本,性能相同或更好,我希望你能分享它。

所以很明显,他并没有试图为这份工作编写最好的代码,只是为了说明他的观点。

他使用的是缓冲通道,所以我不认为通道在这里做任何特殊的工作,任何正常的字符串切片也会做同样的工作。

最新更新