sync包中的同步原语和channel的使用场景

题目来源:七牛

参考《GO 语言并发之道》

参考 http://www.zzvips.com/article/183814.html

Go 语言中的channel和临界区都是控制并发的一种手段,谈及并发,谈及并发,我们首先要了解竞争条件:

当两个或多个操作必须按正确的顺序执行,而程序并未保证这个顺序,就会发生竞争条件。大多数情况下,这将在所谓的数据中出现,其中一个并发操作尝试读取一个变量,而在某个不确定的时间,另一个并发操作试图写入同一个变量。

那什么是临界区呢?

程序中需要独占访问共享资源的部分就叫临界区。举个例子

  1. var data int
  2. go func(){
  3. data++
  4. }()
  5. if data == 0{
  6. fmt.Println("the value is 0.")
  7. }else{
  8. fmt.Printf("the value is %v.
  9. ",data)
  10. }

在这个例子中我们有三个临界区

  • goroutine正在使数据变量递增
  • if 语句,他检查数据的值是否为0
  • fmt.Printf语句,在检索并打印变量的值。

如何解决因为多个goroutine在同一地址空间上运行而产生的数据竞争问题:那就引入了我们的主角,1. 通过sync包的同步原语访问共享内存 2 使用CSP原语来通信共享内存。

sync包含的对低级别内存访问同步最有用的并发原语:

  • WaitGroup(等待一组并发操作完成的方法,尚硅谷有视频称“集齐七颗龙珠召唤神龙”非常形象,可以想象它是一组并发安全的计数器)
  • 互斥锁和读写锁
  • Cond (一般可以认为是事件触发变量,提供signal 和 broadcast 两种方法,当达到某个条件的时候,触发唤醒单个或所有阻塞的goroutine)
  • Once(确保即使在不同的goroutine上,也只会调用一次Do方法处理的函数)

Go重要的主角channel使用的场景

  • 使用chan struct{}作为信号channel
  • 管道实现互斥锁(利用无缓冲通道的同步性质)
  • 使用channel控制子协程( 父协程可以等待子协程结束,父协程也可以向管道中写入数据通知子协程结束,这时子协程需要定期地探测管道中是否有消息出现。)

注意: 使用channel来控制子协程的优点是实现简单,缺点是当需要大量创建协程时就需要有相同数量的channel,而且对于子协程继续派生出来的协程不方便控制。