经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
使goroutine同步的方法总结
来源:cnblogs  作者:xiaoxlm  时间:2018/10/9 10:09:45  对本文有异议

前言:

在前面并发性能对比的文章中,我们可以看到Golang处理大并发的能力十分强劲,而且开发也特别方便,只需要用go关键字即可开启一个新的协程。

但当多个goroutine同时进行处理的时候,就会遇到同时抢占一个资源的情况(并发都会遇到的问题),所以我们希望某个goroutine等待另一个goroutine处理完某一个步骤之后才能继续。sync包就是为了让goroutine同步而出现的。当然还可以使用channel实现,这个后面会介绍到。

锁:

锁有两种:互斥锁(mutex)和读写锁(RWMutex)

互斥锁: 当数据被加锁了之后,除次外的其他协程不能对数据进行读操作和写操作。 这个当然能解决并发程序对资源的操作。但是,效率上是个问题,因为当加锁后,其他协程只有等到解锁后才能对数据进行读写操作。

读写锁: 读数据的时候上读锁,写数据的时候上写锁。有写锁的时候,数据不可读不可写。有读锁的时候,数据可读,不可写。

两种锁的使用方式相同,这里就只列出互斥锁的代码:

  1. package main
  2.  
  3. import (
  4.   "sync"
  5.   "time"
  6.   "fmt"
  7. )
  8.  
  9. var num = 0
  10.  
  11. func main () {
  12.   mu := &sync.Mutex{}
  13.   for i:=0;i<10000;i++ {
  14.     go func(){
  15.       mu.Lock()
  16.       defer mu.Unlock()
  17.       num += 1
  18.     }()
  19.   }
  20.   time.Sleep(time.Second)
  21.   fmt.Println("num:", num) // 如果不加锁这里的num的值会是一个随机数而不是10000
  22. }

 

Once:

有的时候,我们启动多个相同goroutine,但是里面的某个操作我只希望被执行一次,这个时候Once就上场了。

  1. package main
  2.  
  3.  
  4. import (
  5.   "fmt"
  6.   "sync"
  7.   "time"
  8. )
  9.  
  10. func main() {
  11.   var once sync.Once
  12.   one := func() {
  13. fmt.Println("just once")
  14.   }
  15.  
  16.   for i := 0; i < 10; i++ {
  17. go func(a int) {
  18.   once.Do(one) // 只是被执行一次
  19. }(i)
  20.   }
  21.   time.Sleep(time.Millisecond*200)
  22. }

 

WaitGroup:

当某个操作或是某个goroutine需要等待一批goroutine执行完毕以后才继续执行,那么这种多线程(go里面说的线程就是goroutine)等待的问题就可以使用WaitGroup了。

代码如下:

  1. package main
  2.  
  3. import (
  4. "sync"
  5. "fmt"
  6. "time"
  7. )
  8.  
  9. var waitGroup sync.WaitGroup
  10.  
  11. func main () {
  12. for i := 0; i < 10; i++ {
  13. waitGroup.Add(1) // 添加需要等待goroutine的数量
  14. go func() {
  15. fmt.Println("hehe")
  16. time.Sleep(time.Second)
  17. waitGroup.Done() // 减少需要等待goroutine的数量 相当于Add(-1)
  18. } ()
  19. }
  20.  
  21. waitGroup.Wait() // 执行阻塞,直到所有的需要等待的goroutine数量变成0
  22. fmt.Println("over")
  23. }

 

Cond:

sync.Cond是用来控制某个条件下,goroutine进入等待时期,等待信号到来,然后重新启动。

代码如下:

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "sync"
  6. "time"
  7. )
  8. var locker = new(sync.Mutex)
  9. var cond = sync.NewCond(locker)
  10.  
  11. func test(x int) {
  12. cond.L.Lock() //获取锁
  13. cond.Wait()//等待通知 暂时阻塞
  14. fmt.Println(x)
  15. time.Sleep(time.Second * 1)
  16. cond.L.Unlock()//释放锁
  17. }
  18. func main() {
  19. for i := 0; i < 40; i++ {
  20. go test(i)
  21. }
  22. fmt.Println("start all")
  23. time.Sleep(time.Second * 3)
  24. fmt.Println("signal1")
  25. cond.Signal() // 下发一个通知随机给已经获取锁的goroutine
  26. time.Sleep(time.Second * 3)
  27. fmt.Println("signal2")
  28. cond.Signal()// 下发第二个通知随机给已经获取锁的goroutine
  29. time.Sleep(time.Second * 1) // 在广播之前要等一会,让所有线程都在wait状态
  30. fmt.Println("broadcast")
  31. cond.Broadcast()//下发广播给所有等待的goroutine
  32. time.Sleep(time.Second * 60)
  33. }

上面代码有几个要点要特别说明一下:

1. 每个Cond都必须有个与之关联的锁  // 见第9行

2. 协程方法里面一开始/结束都必须加/解锁 // 见第12行和16行

3. cond.Wait()时会自动解锁,当被唤醒时,又会加上锁。所以第2点提到必须加/解锁。

 

Channel

channel不仅可以用来goroutine之间的通信,也可以使goroutine同步完成协作。这点主要基于从channel取数据的时候,会阻塞当前goroutine这个特性。示例代码如下:

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "time"
  6. )
  7.  
  8.  
  9. var chan1 = make(chan string, 512)
  10.  
  11. var arr1 = []string{"qq","ww","ee","rr","tt"}
  12.  
  13. func chanTest1() {
  14. for _, v := range arr1 {
  15. chan1 <- v
  16. }
  17. close(chan1) // 关闭channel
  18. }
  19.  
  20. func chanTest2() {
  21. for {
  22. getStr, ok := <- chan1 // 阻塞,直到chan1里面有数据
  23. if !ok { // 判断channel是否关闭或者为空
  24. return
  25. }
  26. fmt.Println(getStr) // 按数组顺序内容输出
  27. }
  28. }
  29.  
  30. func main () {
  31. go chanTest1()
  32. go chanTest2()
  33.  
  34. time.Sleep(time.Millisecond*200)
  35. }

 

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号