协程基础请参考上一章,我们接着来看下协程后面的部分,协程间通信。
共享变量方式
我们协程间通信,可以采用共享变量的方式,不过得注意数据互斥,例如: 这里有一个小栗子,我们定义一个共享变量,赋初值为0,我们开启50个协程,每个协程对该值 +1,我们来看看具体效果。
我们执行程序后数据如下
为什么数据会不一样呢? 这是因为我们没有对result
这个变量进行加锁,多个协程使用同一变量,但凡有一个协程用的是老的数据,执行结果也会修改最终值,所以才会导致这样的情况,为了避免这种情况,我们仅需对该函数加锁即可,例如: 我们将上述程序修改为如下
加入sync.Mutex
,修改程序如下
我们再次执行程序,数据如下
我们可以将上述加锁的关系简单理解为
除此之外,go sync
还提供了另外一种锁sync.RWMutex
,这是一种读写锁,适用于那些读多,写少。
管道方式
在go
中,协程间通信,很少用共享变量的方式,而更多的方式是使用管道,管道关键字chan
初探chan
我们一般使用make
来申请管道,例如,我们申请一个不带缓存int
类型的管道:
ch := make(chan int)
那么管道是如何进行读写的呢? go
给我提供了相关方式
我们读写管道的内容是需要使用 <-
,例如,将管道的内容赋值给变量x
。
x := <-ch
例如,我们将3传入给管道ch
ch <- 3
对于不带缓存的管道,我们在未写入之前进行读取的话,会发生等待,例如如下例子。
执行结果
通过昨天学习协程可知,主协程中开启其他协程,当主协程执行完毕后,会关闭其他协程,为了避免协程未执行完被关闭,我们了解到第一种方式是加入计算量,例如:sync.WaitGroup
,其第二种方式便是使用管道,实际例子如上。
无缓冲chan 和 有缓冲chan
上述例子介绍的便是无缓冲chan
,其申请方式为make(chan int)
或则make(chan int,0)
,而我们使用make
申请的时候,其size
不为0,则申请的是有缓冲chan
,二者区别为
无缓冲chan
,在写入之前,读取会一直等待,而有缓冲chan
在使用量 < size
则不会等待,而后会一直等待,对于有缓冲chan
,我们可以使用 len
来获取当前chan
使用量,使用cap
来获取总chan
长度,例如:
我们执行代码后,可知
其他使用方法和无缓冲chan
一致。
小案例
我们可以用管道来模拟一下生产者和消费者模型,例如,我们将模型规划为
我们使用管道,能够顺利写出这个demo
我们执行程序结果为
总结
管道(chan
)作为我们常用的协程间通信方式之一,我们通常使用make
来定义chan
,类型可以分为有缓冲和无缓冲2种方式,对于有缓冲管道而言,我们可以写入多个值,直至len
== cap
容量则会等待,等待有操作向管道读取才会继续写入,通过上述案例就可以看得出,最后注意一旦,使用完管道后,要记得使用close
关闭掉。