展示如何使用无缓冲的通道创建一个goroutine池,控制并发频率
1.无缓冲通道保证了两个goroutine之间的数据交换
2.当所有的goroutine都忙的时候,能够及时通过通道告知调用者
3.无缓冲的通道不会有工作在队列里丢失或卡住
4.创建一个工作池,比如这时候会创建出2个goroutine,被一个无缓冲通道阻塞住,等待在那里,除非通道关闭,在当前的gorotine上会无限循环读取通道,不会退出
5.当有一堆的任务goroutine被发送过来的时候,会先传送给那一个通道,这时候不管有多少个,都会阻塞并等待上面那俩工作完,就起到了控制并发的目的

  1. package main
  2.  
  3. import (
  4. "log"
  5. "sync"
  6. "time"
  7. )
  8.  
  9. //定义一个Worker接口,有个必须实现的Task()方法
  10. type Worker interface {
  11. Task()
  12. }
  13.  
  14. //定义一个类型Pool,有两个成员
  15. type Pool struct {
  16. //成员work,通道类型,传递的是Worker类型
  17. work chan Worker
  18. //成员wg是计数信号量
  19. wg sync.WaitGroup
  20. }
  21.  
  22. //定义New方法,返回的是Pool实例
  23. //传递的参数是goroutine池的数量
  24. func New(size int) *Pool {
  25. //实例化Pool类型
  26. pool := Pool{
  27. work: make(chan Worker),
  28. }
  29. //增加计数信号量
  30. pool.wg.Add(size)
  31. //使用循环创建多个goroutine
  32. for i := 0; i < size; i++ {
  33. //启动goroutine
  34. go func() {
  35. //从通道中获取值,这里如果没有会一直阻塞
  36. //这里会无限循环遍历,除非通道关闭了,否则不会跳出当前这个goroutine
  37. for w := range pool.work {
  38. //调用Worker类型的Task()方法
  39. w.Task()
  40. }
  41. }()
  42. pool.wg.Done()
  43. }
  44. return &pool
  45. }
  46.  
  47. //给Pool类型定义Run方法
  48. //参数是Worker类型
  49. func (p *Pool) Run(w Worker) {
  50. //把Worker传进通道里
  51. p.work <- w
  52. }
  53.  
  54. //给Pool类型定义 Shutdown方法
  55. func (p *Pool) Shutdown() {
  56. //关闭通道
  57. close(p.work)
  58. //等待所有goroutine执行结束
  59. p.wg.Wait()
  60. }
  61.  
  62. //定义一个字符串数组
  63. var names = []string{
  64. "zhangsan",
  65. "lisi",
  66. "wangwu",
  67. }
  68.  
  69. //定义一个类型namePrinter
  70. type namePrinter struct {
  71. //成员name ,字符串类型
  72. name string
  73. }
  74.  
  75. //给类型实现Worker接口
  76. func (np *namePrinter) Task() {
  77. //打印namePrinter类型的name成员
  78. log.Printf(np.name)
  79. //睡眠一秒
  80. time.Sleep(time.Second)
  81. }
  82. func main() {
  83. //创建2个goroutine的池,因为通道是空的,这个地方有两个goroutine会阻塞在那
  84. pool := New(2)
  85. //定义计数信号量
  86. var wg sync.WaitGroup
  87. //增加计数,100次乘以数组元素个数
  88. wg.Add(100 * len(names))
  89. //循环100次,这个地方会瞬间生成300个goroutine,大并发的去执行任务
  90. for i := 0; i < 100; i++ {
  91. //循环数组
  92. for _, name := range names {
  93. //实例化namePrinter类型
  94. np := namePrinter{
  95. name: name,
  96. }
  97. //启动一个goroutine
  98. go func() {
  99. //调用Pool类型的run方法
  100. //传递的是Woker类型,因此要取地址
  101. //这里会把该Worker类型,发送到通道里,如果通道不为空,就会阻塞住
  102. //当300个goroutine,把name传递给run方法,会因为通道不为空被阻塞住
  103. //通道何时才能为空呢,也就只有在工作池里的goroutine把通道读走
  104. //因此会每次两个两个的打印,最多只会等待两个工作的完成
  105. pool.Run(&np)
  106. wg.Done()
  107. }()
  108. }
  109. }
  110. //等待上面的100次遍历结束
  111. wg.Wait()
  112. //停止工作池,关闭通道
  113. pool.Shutdown()
  114. }

  

[Go] golang无缓冲通道实现工作池控制并发的更多相关文章

  1. golang channel无缓冲通道会发生阻塞的验证

    公司搞了午间技术par,本周我讲的主题是关于无缓冲通道channel是否会发生阻塞,并进行了验证. go语言中channel分为无缓冲通道和有缓冲通道两种 channel提供了一种在goroutine ...

  2. Golang并发编程有缓冲通道和无缓冲通道(channel)

    无缓冲通道 是指在接收前没有能力保存任何值得通道.这种类型的通道要求发送goroutine和接收goroutine同时准备好,才能完成发送和接收操作.如果两个goroutine没有同时准备好,通道会导 ...

  3. golang中为何在同一个goroutine中使用无缓冲通道会导致死锁

    package main import "fmt" func main() { /* 以下程序会导致死锁 c := make(chan int) c <- 10 n1 := ...

  4. Go语言的通道(1)-无缓冲通道

    前言: 上文中我们采用了[原子函数]已经[共享锁]两种方式分别对多个goroutine进行了同步,但是在go语言中提供了另一种更好的方式,那就是使用通道(Channel). 一.通道是什么? 其实无论 ...

  5. [GO]无缓冲通道(unbuffered channel)

    无缓冲通道(unbuffered channel)是指在接收前没有能力保存任何值的通道,在之前的例子中使用的都是无缓冲通道,需要注意的是,对于无缓冲通道而言,不管是往通道里写数据还是从通道里读数据,都 ...

  6. golang 无缓冲channel

    golang 无缓冲channel package main import "fmt" func main() { // 1S =1000ms //1ms = 1000us //1 ...

  7. go无缓冲通道

    package main import ( "fmt" "math/rand" "sync" "time" ) //wg ...

  8. Go语言有缓冲和无缓冲通道实现样例

    感觉可以,但不好用. 应该有封装程序更高的包包吧. package main import ( "math/rand" "fmt" "time&quo ...

  9. [Go] golang缓冲通道实现管理一组goroutine工作

    通道1.当一个资源需要在goroutine之间共享时,通道在goroutine之间架起了一个管道2.无缓冲通道和有缓冲通道,make的第二个参数就是缓冲区大小3.无缓冲通道需要发送和接收都准备好,否则 ...

随机推荐

  1. [LeetCode] Score of Parentheses 括号的分数

    Given a balanced parentheses string S, compute the score of the string based on the following rule: ...

  2. 兼容IE8,滚动加载下一页

    // 滚动加载下一页         var nowScrolledHeight = document.documentElement.scrollTop || document.body.scrol ...

  3. Mesos源码分析(15): Test Executor的运行

    Test Executor的代码在src/examples/test_executor.cpp中   int main(int argc, char** argv) {   TestExecutor ...

  4. 【开源项目】电视盒子好用又强大的APP: TVRemoteIME

    TVRemoteIME 电视盒子的远程输入法应用,可跨屏远程输入.跨屏远程控制盒子.远程文件管理.HTTP/RTMP/MMS网络视频直播.ED2K/种子文件的视频文件边下边播 应用的诞生 自从家里有电 ...

  5. [Swift]LeetCode354. 俄罗斯套娃信封问题 | Russian Doll Envelopes

    You have a number of envelopes with widths and heights given as a pair of integers (w, h). One envel ...

  6. [Swift]LeetCode500. 键盘行 | Keyboard Row

    Given a List of words, return the words that can be typed using letters of alphabet on only one row' ...

  7. [Swift]LeetCode862. 和至少为 K 的最短子数组 | Shortest Subarray with Sum at Least K

    Return the length of the shortest, non-empty, contiguous subarray of A with sum at least K. If there ...

  8. [Swift]LeetCode899. 有序队列 | Orderly Queue

    A string S of lowercase letters is given.  Then, we may make any number of moves. In each move, we c ...

  9. PHP常用设计模式讲解

    开发中适当的使用设计模式,可以让项目有更易扩展,易维护.低耦合,代码简洁等 单例模式 <?php /** * 单例模式:使类在全局范围内只允许创建一个对象,常用于数据库连接等 */ class ...

  10. Shell脚本中的for case while循环流程控制语句的使用

    shell作为一种脚本编程语言,同样包含循环.分支等其他程序控制结构,从而轻松完成更加复杂.强大的功能. 编写脚本的思路 1  明确脚本的功能 2  编写脚本时会使用到那些命令 ?   3  把变化的 ...