我正在研究一个关于使用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的并发版本时,可以采用几种方法和选项。此时不要对我的特定实现感到困惑。如果你有一个可读性更强的版本,性能相同或更好,我希望你能分享它。
所以很明显,他并没有试图为这份工作编写最好的代码,只是为了说明他的观点。
他使用的是缓冲通道,所以我不认为通道在这里做任何特殊的工作,任何正常的字符串切片也会做同样的工作。