经过几日的学习后,本人总结出两种可以实现线程间同步的方法:
sync.WaitGroup
类型- 管道
channel
- 关闭管道
- 带缓冲区的管道
- 没有缓存区的管道
其中,sync.WaitGroup
和带缓冲区的管道适用于多个并行线程简得同步,其他的方法只适用于两个线程的同步
sync.WaitGroup
基本方法
1 | type WaitGroup struct { |
WaitGroup
用于等待一组线程的结束。父线程调用Add方法来设定应等待的线程的数量。每个被等待的线程在结束时应调用Done方法。同时,主线程里可以调用Wait方法阻塞至所有线程结束。
1 | func (wg *WaitGroup) Add(delta int) |
Add
方法向内部计数加上delta
,delta
可以是负数;如果内部计数器变为0,Wait方法阻塞等待的所有线程都会释放,如果计数器小于0,方法panic。
1 | func (wg *WaitGroup) Done() |
Done
方法减少WaitGroup
计数器的值,应在线程的最后执行。
1 | func (wg *WaitGroup) Wait() |
Wait
方法阻塞直到WaitGroup
计数器减为0。
总结
WaitGroup
对象内部有一个计数器,最初从0开始,它有三个方法:Add(), Done(), Wait()
用来控制计数器的数量。
Add(n)
把计数器设置为n
Done()
每次把计数器-1
wait()
会阻塞代码的运行,直到计数器地值减为0
管道
基本方法
关闭管道
当线程完成工作后关闭管道
1 | close(ch) |
主线程不断循环测试ok,在关闭管道并且管道中没有数据时,ok会为false,即可跳出循环
1 | // 不断循环测试ok |
有缓冲区的管道
使用一个有缓冲区的管道作为进程间同步的方式时,
- 在需要阻塞的位置处尝试从管道中读取数据
<-ch
,若管道中没有数据会暂时阻塞当前线程。 - 在另一线程工作完成后,向管道中发送数据
ch <- 1
,由于管道中有了数据就可以唤醒线程继续执行,实现同步
无缓冲区的管道
使用无缓冲区的管道作为进程同步的方式时,
- 在需要阻塞的位置处尝试向管道中写入数据
ch <- 1
,因为没有其他读线程,所以当前线程会被暂时阻塞 - 在另一线程工作完成后,尝试从管道中读取数据
<-ch
,由于有了读线程,被阻塞的线程被唤醒继续执行,实现同步
实例
利用多个协程求1-80000的所有素数
1 | package main |