博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使goroutine同步的方法总结
阅读量:3986 次
发布时间:2019-05-24

本文共 3125 字,大约阅读时间需要 10 分钟。

前言:

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

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

锁:

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

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

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

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

package mainimport (  "sync"  "time"  "fmt")var num = 0func main ()  {
  mu := &sync.Mutex{
}  for i:=0;i<10000;i++ {
    go func(){
      mu.Lock()      defer mu.Unlock()      num += 1    }()  }  time.Sleep(time.Second)  fmt.Println("num:", num) // 如果不加锁这里的num的值会是一个随机数而不是10000}

Once:

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

package mainimport (  "fmt"  "sync"  "time")func main() {
  var once sync.Once  one := func() {
fmt.Println("just once")  }  for i := 0; i < 10; i++ {
go func(a int) {
  once.Do(one) // 只是被执行一次 }(i)  }  time.Sleep(time.Millisecond*200)}

WaitGroup:

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

代码如下:

package mainimport (    "sync"    "fmt"    "time")var waitGroup sync.WaitGroupfunc main () {
for i := 0; i < 10; i++ {
waitGroup.Add(1) // 添加需要等待goroutine的数量 go func() {
fmt.Println("hehe") time.Sleep(time.Second) waitGroup.Done() // 减少需要等待goroutine的数量 相当于Add(-1) } () } waitGroup.Wait() // 执行阻塞,直到所有的需要等待的goroutine数量变成0 fmt.Println("over")}

Cond:

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

代码如下:

package mainimport (    "fmt"    "sync"    "time")var locker = new(sync.Mutex)var cond = sync.NewCond(locker)func test(x int) {
cond.L.Lock() //获取锁 cond.Wait()//等待通知 暂时阻塞 fmt.Println(x) time.Sleep(time.Second * 1) cond.L.Unlock()//释放锁}func main() {
for i := 0; i < 40; i++ {
go test(i) } fmt.Println("start all") time.Sleep(time.Second * 3) fmt.Println("signal1") cond.Signal() // 下发一个通知随机给已经获取锁的goroutine time.Sleep(time.Second * 3) fmt.Println("signal2") cond.Signal()// 下发第二个通知随机给已经获取锁的goroutine time.Sleep(time.Second * 1) // 在广播之前要等一会,让所有线程都在wait状态 fmt.Println("broadcast") cond.Broadcast()//下发广播给所有等待的goroutine time.Sleep(time.Second * 60)}

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

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

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

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

Channel

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

package mainimport (    "fmt"    "time")var chan1 = make(chan string, 512)var arr1 = []string{
"qq","ww","ee","rr","tt"}func chanTest1() {
for _, v := range arr1 {
chan1 <- v } close(chan1) // 关闭channel}func chanTest2() {
for {
getStr, ok := <- chan1 // 阻塞,直到chan1里面有数据 if !ok {
// 判断channel是否关闭或者为空 return } fmt.Println(getStr) // 按数组顺序内容输出 }}func main () {
go chanTest1() go chanTest2() time.Sleep(time.Millisecond*200)}

转载地址:http://rdaui.baihongyu.com/

你可能感兴趣的文章
自定义Viewgroup实现流式布局(3):条件换行的自定义ViewGroup
查看>>
自定义Viewgroup实现流式布局(3):实现流式布局
查看>>
React-native封装view
查看>>
React-native Alert弹出多个按钮
查看>>
Android圆形角标
查看>>
GetSafeHwnd()的调用正确时机
查看>>
coursesa课程 Python 3 programming course_2_assessment_1
查看>>
coursesa课程 Python 3 programming 统计文件有多少单词
查看>>
coursesa课程 Python 3 programming 输出每一行句子的第三个单词
查看>>
coursesa课程 Python 3 programming Dictionary methods 字典的方法
查看>>
coursesa课程 Python 3 programming Accumulating Multiple Results In a Dictionary 统计文件的字母数量
查看>>
Returning a value from a function
查看>>
coursesa课程 Python 3 programming Functions can call other functions 函数调用另一个函数
查看>>
coursesa课程 Python 3 programming Tuple Assignment with Unpacking
查看>>
coursesa课程 Python 3 programming The while Statement
查看>>
course_2_assessment_6
查看>>
coursesa课程 Python 3 programming course_2_assessment_7 多参数函数练习题
查看>>
coursesa课程 Python 3 programming 排序函数sorted的可选参数
查看>>
coursesa课程 Python 3 programming course_2_assessment_8 sorted练习题
查看>>
visca接口转RS-232C接口线序
查看>>