中文:Go程序设计语言 2017.1
英文:The Go Programming Language 2016
8.4.2. Pipelines
8.4.2管道章节
修正:
第一个管道应该改成通道,cannel是概念词汇
重要概念词汇不能混淆
来自chatGPT3.5:
Go Pipelines和channel都是Go语言中用于实现并发编程的重要特性,但它们之间有一些区别。
首先,Go Pipelines是一种将多个阶段的处理连接在一起的方式,每个阶段都是一个goroutine,它们通过channel进行通信。Go Pipelines通常用于处理数据流,例如从文件或网络读取数据,对数据进行处理,然后将结果写回文件或网络。Go Pipelines的优点是可以将处理过程分解为多个阶段,每个阶段都可以并行执行,从而提高处理效率。
相比之下,channel是一种用于在goroutine之间进行通信的机制。通过channel,一个goroutine可以向另一个goroutine发送数据,也可以从另一个goroutine接收数据。channel的优点是可以实现goroutine之间的同步和通信,从而避免了共享内存带来的并发问题。
因此,Go Pipelines和channel都是Go语言中用于实现并发编程的重要特性,但它们的应用场景略有不同。Go Pipelines通常用于处理数据流,而channel则用于实现goroutine之间的同步和通信。
Go Pipelines通常包含多个goroutine和多个channel。在Go Pipelines中,每个阶段都是一个goroutine,它们通过channel进行通信。每个阶段通常会有一个输入channel和一个输出channel,用于接收上一阶段的输出和向下一阶段发送数据。因此,Go Pipelines通常包含多个goroutine和多个channel,它们协同工作以完成数据处理任务
修正:
删除“因为通道会满”(原文没有提及)
我个人不太喜欢这个简单的解释,
我的理解(参考chat):
-
如果放在主goroutine循环前,具体执行来说:
当 for f := range filenames 循环开始执行时,它会从 filenames 通道中获取一个文件名,然后将该文件名发送到 jobs 通道中,表示需要处理该文件。然后,它会等待 worker goroutine 向 sizes 通道发送一个消息,表示该文件已经处理完毕。但是,由于 wg.Wait() 的计数器一直没有被归零(因为wg.Add(1)执行了),阻塞,worker goroutine 无法向 sizes 通道发送消息,因此 for f := range filenames 循环会一直等待下去,从而导致程序陷入死锁状态。 -
如果放在主goroutine循环后,因为如果没有go,sizes管道只能放入一个内容,而wg.Wait()会阻塞,导致无法执行到for size := range sizes(循环去除放入的管道内容),
//此时被放入管道内容就一直没人拿出来,导致后面goroutine也一直放不进去,卡死
补充下更详细的说明:
func makeThumbnails6(filenames <-chan string) int64 {
sizes := make(chan int64)
var wg sync.WaitGroup // number of working goroutines
for f := range filenames {
wg.Add(1) //必须在goroutine开始前调用,我们无法确保Add发送在close之前
// worker
go func(f string) {
defer wg.Done() //使用defer来确保即使出现错误,计数器也会递减
thumb, err := thumbnail.ImageFile(f)
if err != nil {
log.Println(err)
return
}
info, _ := os.Stat(thumb) // OK to ignore error
sizes <- info.Size()
}(f)
}
//必须和上面的循环并发执行,否则会导致卡死,
//因为如果没有go,sizes管道只能放入一个内容,而wg.Wait()会阻塞,导致无法执行到for size := range sizes(循环去除放入的管道内容),
//此时被放入管道内容就一直没人拿出来,导致后面goroutine也一直放不进去,卡死
go func() {
wg.Wait() //阻塞当前的 goroutine 直到计数器被归零(当计数器减为 0 时,允许程序继续执行下去)
close(sizes)
}()
var total int64
// 这里不是循环sizes数组,是循环获取sizes的管道信息,每次goroutine执行完就会插入一个数据,循环就会进行读取
for size := range sizes {
total += size
}
return total
}
正确顺序: