Go天生支持多线程,但是会有一些很有意思的事情值得思考

目录

探索一:同时开启两个线程不加任何保护措施

探索二:去掉sleep直接运行

探索三:强行在main中添加sleep,完成实验效果但是仍不安全

通道(channel)

通道缓冲区

Go 遍历通道与关闭通道

解决方法一:引入信道

解决方法二:sync.WaitGroup

参考


探索一:同时开启两个线程不加任何保护措施

开启两个线程,中间sleep100ms,出现线程未执行完毕,func main已经结束!

所以此次运行会不打印任何值程序运行结束!

探索二:去掉sleep直接运行

将sleep去掉,两个线程只能随机运行部分后,func main已经运行结束!

所以这次的结果会随机打印一些step和index!也有可能任何都打印不出来main函数就已经结束!

探索三:强行在main中添加sleep,完成实验效果但是仍不安全

仍不安全的写法,index=1时尚未运行完main结束,即使加上func main中加入sleep可以达到预期效果,但是治标不治本!

要解决这个问题,先学习一些go特有的通道:

通道(channel)

通道(channel)是用来传递数据的一个数据结构。

通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。

通道使用前必须先创建:

注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。

通道缓冲区

通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小:

带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。

不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。

注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞

Go 遍历通道与关闭通道

Go 通过 range 关键字来实现遍历读取到的数据,类似于与数组或切片。格式如下:

如果通道接收不到数据后 ok 就为 false,这时通道就可以使用 close() 函数来关闭。

执行输出结果为:

1
2
3
4
5
6
7
8
9
10
0
1
1
2
3
5
8
13
21
34

解决方法一:引入信道

引入一个信道,默认情况下,信道的存消息和取消息都是阻塞的,在 goroutine 中执行完成后给信道一个值 0,则主函数会一直等待信道中的值,一旦信道有值,主函数才会结束。

解决方法二:sync.WaitGroup

sync包提供了基本的同步基元,如互斥锁。除了Once和WaitGroup类型,大部分都是适用于低水平程序线程,高水平的同步使用channel通信更好一些。

WaitGroup用于等待一组线程的结束。父线程调用Add方法来设定应等待的线程的数量。每个被等待的线程在结束时应调用Done方法。同时,主线程里可以调用Wait方法阻塞至所有线程结束。

参考

https://www.runoob.com/go/go-concurrent.html

《Go语言实战》

https://studygolang.com/pkgdoc