Golang中的并发性是指函数独立运行的能力。Goroutines就是能够并发运行的函数,也叫协程,它们是Golang提供作为并发处理操作的方法。
使用go语句创建协程goroutines
要将函数作为goroutine运行,以go语句为前缀调用函数。下面是示例代码块:
sum() // 正常的函数调用,该函数执行完返回后再执行下一条语句,这是同步调用
go sum() // 作为协程的函数调用,不等该函数执行完成直接执行下一条语句,这是异步调用
关键字go调用函数后立即返回,而函数作为协程在后台运行,程序的其余部分同时继续执行。每个Golang程序的main函数都是作为协程启动的,因此每个Golang程序至少运行一个goroutine。
创建协程
在每次调用函数responseSize之前添加go关键字。以下示例代码中有三个responseSize协程同时启动,对http.Get的三个调用也同时进行。程序不会等到一个响应返回后再发送下一个请求。因此,使用goroutines可以快速地打印三个响应的长度。
![71b7cd95f9e7e33b703496c1cf55b6a4.png](https://i-blog.csdnimg.cn/blog_migrate/64c5cf01c1d1d8e41a96c40020ec25a2.jpeg)
结果:
C:Golang>go run main.go
Step1: https://www.baidu.com
Step1: https://www.sina.com.cn/
Step1: https://www.toutiao.com/
Step2: https://www.sina.com.cn/
Step3: https://www.sina.com.cn/
Step4: 116749
Step2: https://www.baidu.com
Step3: https://www.baidu.com
Step4: 79551
Step2: https://www.toutiao.com/
Step3: https://www.toutiao.com/
Step4: 203842
在main函数中添加了一个time.Sleep的方法,它可以防止main函数在协程结束之前退出。调用time.Sleep(10*time.Second)将使main协程睡眠10秒。
等待协程执行完成
sync包的中WaitGroup类型用来等待程序执行完所有在主函数中启动的协程。它维护一个记录协程数量的计数器,WaitGroup的Wait方法阻塞程序的执行,直到WaitGroup计数器为零,程序才继续往下执行。
调用WaitGroup的Add方法用于向WaitGroup添加计数器。
在协程里面使用defer语句调用WaitGroup的Done方法来减少WaitGroup计数器。
调用WaitGroup的Wait方法等待程序完成所有协程。
在主函数内部调用Wait方法,该方法阻止程序执行,直到WaitGroup计数器达到0的值,确保执行完所有协程。
![e12aeb48b68ae181303add24db8cf733.png](https://i-blog.csdnimg.cn/blog_migrate/a44192daaddd99faa1ba9afe00eb5489.jpeg)
结果:
C:Golang>go run main.go
Start Goroutines
Step1: https://www.toutiao.com/
Step1: https://www.baidu.com
Step1: https://www.sina.com.cn/
Step2: https://www.sina.com.cn/
Step3: https://www.sina.com.cn/
Step4: 116749
Step2: https://www.baidu.com
Step3: https://www.baidu.com
Step4: 79801
Step2: https://www.toutiao.com/
Step3: https://www.toutiao.com/
Step4: 203842
从协程里获取值
从协程里获取值的最便捷的方法是通道。通道是连接并发性协程的管道。将值从一个协程里发送到通道中,另一个协程或同步函数再从通道里接收这些值。
![f1b0beedb9030b9ee36bb3c42e153bda.png](https://i-blog.csdnimg.cn/blog_migrate/6d76a33204650342a2b6787776bc8c0a.jpeg)
结果:
C:Golang>go run main.go
79655
运行和暂停协程的执行
使用管道可以运行和暂停协程的执行。管道就像是协程之间的协调者,用来进行这些操作。
![796b2d80c676cbc185e7dc4f8e36b26a.png](https://i-blog.csdnimg.cn/blog_migrate/1341a781aa08abbe38968bbf5ad7e500.jpeg)
结果:
1
2
3
4
Pause
Play
5
6
7
8
9
Stop
使用原子函数修复竞争条件
由于对共享资源的访问不同步,并试图同时对该资源进行读写,因此会出现竞争使用资源的情况。
原子函数提供同步访问整型和指针的低级锁定机制。原子函数通常用于修复竞争条件。
sync包中atomic子包的函数通过锁定对共享资源的访问来支持同步协程。
![712ccc7daa42b08d9b873b8b6bf59ed9.png](https://i-blog.csdnimg.cn/blog_migrate/b2c7c08983cfed6060a0d8cdfaf9729f.jpeg)
atomic包中的AddInt32函数通过强制一次只有一个协程可以执行和完成加法操作来同步整型值的更新。当协程试图调用任何atomic包的函数时,它们会自动地同步访问被限定的变量。
结果:
C:Golang>go run -race main.go
Counter: 15
如果使用counter++代替atomic.AddInt32(&counter, 1),则结果如下:
C:Golang>go run -race main.go
==================
WARNING: DATA RACE
Read at 0x0000006072b0 by goroutine 7:
main.increment()
C:/Golang/main.go:31 +0x76
Previous write at 0x0000006072b0 by goroutine 8:
main.increment()
C:/Golang/main.go:31 +0x90
Goroutine 7 (running) created at:
main.main()
C:/Golang/main.go:18 +0x7e
Goroutine 8 (running) created at:
main.main()
C:/Golang/main.go:19 +0x96
==================
Counter: 15
Found 1 data race(s)
exit status 66
使用互斥锁Mutex定义关键代码
互斥锁用于创建关键代码,关键代码内访问资源,从而确保一次只能有一个协程执行该部分代码。
![f428c5837ed01fb97f466781be7dd7a9.png](https://i-blog.csdnimg.cn/blog_migrate/f007d04094df3a9ce715672a83932592.jpeg)
结果:
C:Golang>go run -race main.go
PHP stands for Hypertext Preprocessor.
PHP stands for Hypertext Preprocessor.
The Go Programming Language, also commonly referred to as Golang
The Go Programming Language, also commonly referred to as Golang
Counter: 4
由互斥锁的Lock()方法和Unlock()定义关键代码保护对计数器的更新。