go中使用并发
goroutine必定对应一个函数,
多个goroutine可以执行相同的函数
Go程序就会为main()函数创建一个默认的goroutine。
一、goroutine的使用
package main
import (
"fmt"
)
func Hello() {
fmt.Println("hello执行完成")
}
func main() {
go Hello()
fmt.Println("main执行完成") // 会消耗时间,所以协程会执行
}
--》
main执行完成
hello执行完成
先执行主协程,其他的协程创建需要时间,所以是先执行main,再执行Hello,那个空闲执行那个
主协程终止,其他协程也会终止
二、多个goroutine
package main
import (
"fmt"
)
func Hello1() {
fmt.Println("Hello1执行完成")
}
func Hi1() {
fmt.Println("Hi1执行完成")
}
func main() {
go Hi1()
go Hello1()
fmt.Println("main执行完成")
time.Sleep(time.Second * 5)
}
goroutine的调度是随机的,除了主协程,其他的协程随机调度
三、sync.WaitGroup的使用
我们不知道协程执行完成需要多少时间,所以使用time.Sleep()不知道给多少时间合适
两种方法:
-
sync.WaitGroup
-
chan:看chan章节
1.介绍
sync:synchronization同步这个词的缩写,所以也会叫做同步包
WaitGroup:同步等待组。内部有一个计数器,从0开始,不能为负数
2.使用
- Add(delta int):设置计数器
- Done():会把计数器-1
- Wait():会阻塞代码的运行,直到计数器地值减为0,为0终止
示例代码:
package main
import (
"fmt"
"sync"
)
var wg = sync.WaitGroup{}
func Hi() {
fmt.Println("Hi执行完成")
wg .Done()
}
func main() {
for i := 0; i < 10; i++ {
wg.Add(1) //每创建一个goroutine,就把任务队列中任务的数量+1
go Hi2() // 这里如果创建两个goroutine呢,签名的Add()里面是2?
}
wg.Wait() //.Wait()这里会发生阻塞,直到队列中所有的任务结束就会解除阻塞
fmt.Println("主协程结束")
}
四、Goroutine池
1.前言
- goroutine虽然很小,但是当无休止的开辟Goroutine会出现高频率的调度Groutine,会浪费很多资源的同时占用很大的内存,怎么办呢?设计一个goroutine池,限制协程上限,重用协程,不要每次都去创建一个新的协程。
2.作用
- 大量的goroutine会占用大量的内存,Goroutine池可以减轻内存负担
- 性能消耗:重复的创建与销毁goroutine,大量的goroutine调度等会占用资源,Goroutine池可以减轻资源消耗
- 减少goroutine创建时间,提高效率
- 管理协程,控制并发量,定期回收等
3.实现思路
-
启动服务的时候初始化一个Goroutine Pool,这个协程池维护了任务的管道和worker。
-
外部将请求投递到Goroutine Pool,Goroutine Pool的操作是:判断当前运行的worker是否已经超过Pool的容量,如果超过就将请求放到任务管道中直到运行的worker将管道中的任务执行;如果没有超过就新开一个worker处理。
4.代码实现
- 任务
- 创建任务
- 执行任务
- 协程池
- 创建协程池
- 任务投递及任务执行(读写chan)
- 协程池开启