Go 缓冲信道和工作池

Go 缓冲信道和工作池,第1张

概述23.缓冲信道工作池(BufferedChannelsandWorkerPools)什么是缓冲信道?在[上一教程]里,我们讨论的主要是无缓冲信道。我们在[信道]的教程里详细讨论了,无缓冲信道的发送和接收过程是阻塞的。我们还可以创建一个有缓冲(Buffer)的信道。只在缓冲已满的情况,才会阻塞向缓冲信道(Buffe 23. 缓冲信道和工作池(Buffered Channels and Worker Pools)什么是缓冲信道?

在[上一教程]里,我们讨论的主要是无缓冲信道。我们在[信道]的教程里详细讨论了,无缓冲信道的发送和接收过程是阻塞的。

我们还可以创建一个有缓冲(Buffer)的信道。只在缓冲已满的情况,才会阻塞向缓冲信道(Buffered Channel)发送数据。同样,只有在缓冲为空的时候,才会阻塞从缓冲信道接收数据。

通过向 make 函数再传递一个表示容量的参数(指定缓冲的大小),可以创建缓冲信道。

copy
ch := make(chan type, capacity)

要让一个信道有缓冲,上面语法中的 capacity 应该大于 0。无缓冲信道的容量默认为 0,因此我们在[上一教程]创建信道时,省略了容量参数。

我们开始编写代码,创建一个缓冲信道。

示例一copy
package mainimport (      "fmt")func main() {      ch := make(chan string, 2)    ch <- "naveen"    ch <- "paul"    fmt.Println(<- ch)    fmt.Println(<- ch)}

在上面程序里的第 9 行,我们创建了一个缓冲信道,其容量为 2。由于该信道的容量为 2,因此可向它写入两个字符串,而且不会发生阻塞。在第 10 行和第 11 行,我们向信道写入两个字符串,该信道并没有发生阻塞。我们又在第 12 行和第 13 行分别读取了这两个字符串。该程序输出:

copy
naveen  paul
示例二

我们再看一个缓冲信道的示例,其中有一个并发的 Go 协程来向信道写入数据,而 Go 主协程负责读取数据。该示例帮助我们进一步理解,在向缓冲信道写入数据时,什么时候会发生阻塞。

copy
package mainimport (      "fmt"    "time")func write(ch chan int) {      for i := 0; i < 5; i++ {        ch <- i        fmt.Println("successfully wrote", i, "to ch")    }    close(ch)}func main() {      ch := make(chan int, 2)    go write(ch)    time.Sleep(2 * time.Second)    for v := range ch {        fmt.Println("read value", v,"from ch")        time.Sleep(2 * time.Second)    }}

在上面的程序中,第 16 行在 Go 主协程中创建了容量为 2 的缓冲信道 ch,而第 17 行把 ch 传递给了 write 协程。接下来 Go 主协程休眠了两秒。在这期间,write 协程在并发地运行。write 协程有一个 for 循环,依次向信道 ch 写入 0~4。而缓冲信道的容量为 2,因此 write 协程里立即会向 ch 写入 0 和 1,接下来发生阻塞,直到 ch 内的值被读取。因此,该程序立即打印出下面两行:

copy
successfully wrote 0 to ch  successfully wrote 1 to ch

打印上面两行之后,write 协程中向 ch 的写入发生了阻塞,直到 ch 有值被读取到。而 Go 主协程休眠了两秒后,才开始读取该信道,因此在休眠期间程序不会打印任何结果。主协程结束休眠后,在第 19 行使用 for range 循环,开始读取信道 ch,打印出了读取到的值后又休眠两秒,这个循环一直到 ch 关闭才结束。所以该程序在两秒后会打印下面两行:

copy
read value 0 from ch  successfully wrote 2 to ch

该过程会一直进行,直到信道读取完所有的值,并在 write 协程中关闭信道。最终输出如下:

copy
successfully wrote 0 to ch  successfully wrote 1 to ch  read value 0 from ch  successfully wrote 2 to ch  read value 1 from ch  successfully wrote 3 to ch  read value 2 from ch  successfully wrote 4 to ch  read value 3 from ch  read value 4 from ch
死锁copy
package mainimport (      "fmt")func main() {      ch := make(chan string, 2)    ch <- "naveen"    ch <- "paul"    ch <- "steve"    fmt.Println(<-ch)    fmt.Println(<-ch)}

在上面程序里,我们向容量为 2 的缓冲信道写入 3 个字符串。当在程序控制到达第 3 次写入时(第 11 行),由于它超出了信道的容量,因此这次写入发生了阻塞。现在想要这次写 *** 作能够进行下去,必须要有其它协程来读取这个信道的数据。但在本例中,并没有并发协程来读取这个信道,因此这里会发生死锁(deadlock)。程序会在运行时触发 panic,信息如下:

copy
Fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan send]:  main.main()      /tmp/sandBox274756028/main.go:11 +0x100
长度 vs 容量

缓冲信道的容量是指信道可以存储的值的数量。我们在使用 make 函数创建缓冲信道的时候会指定容量大小。

缓冲信道的长度是指信道中当前排队的元素个数。

代码可以把一切解释得很清楚。 总结

以上是内存溢出为你收集整理的Go 缓冲信道和工作池全部内容,希望文章能够帮你解决Go 缓冲信道和工作池所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1211762.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-04
下一篇 2022-06-04

发表评论

登录后才能评论

评论列表(0条)

保存