Goroutine是Go运行时管理的轻量级线程
主线程结束时,协程会被中断,需要有效的阻塞机制
协程的使用
package main
import (
"fmt"
"time"
)
// SendCode 发送验证码
func SendCode() {
fmt.Println("发送验证码开始")
time.Sleep(3 * time.Second)
fmt.Println("发送验证码完成!")
}
func main() {
// 实现用户注册功能
fmt.Println("用户注册校验完成")
// 发送验证码
//SendCode() // 会阻塞主线程
go SendCode() // 会阻塞主线程
fmt.Println("验证码已发送,请注意查收...")
}
问题:
- 主线程结束,协程也会结束
- 协程安全
- 如何获取协程函数的返回值,如何在协程中传递数据?
WaitGroup
前面我们通过让主程序延时的方式,可以成功让协程函数顺利结束
但是,延时多久没人能够知道
所以,睡眠这种方式肯定不靠谱
go 自带一个WaitGroup可以解决这个问题, 代码如下
package main
import (
"sync"
)
var wg sync.WaitGroup
func say(s string) {
for i := 0; i < 5; i++ {
println(s)
}
wg.Done()
}
func main() {
wg.Add(2)
go say("Hello")
go say("World")
wg.Wait()
}
wg.add(2)是有2个goroutine需要执行
wg.Done 相当于 wg.Add(-1) 意思就是我这个协程执行完了。wg.Wait() 就是告诉主线程要等一下,等他们2个都执行完再退出
协程安全
非常经典的例子,两个协程函数,分别对同一个全局变量进行操作
按照我们预期的结果,应该是200万,但是多运行几次,会发现结果各不相同
这就是协程安全问题
package main
import (
"fmt"
"sync"
)
var w = sync.WaitGroup{}
var num = 0
func AddNum() {
for i := 0; i < 1000000; i++ {
num++
}
w.Done()
}
func main() {
w.Add(2)
go AddNum()
go AddNum()
w.Wait()
fmt.Println(num)
}
这种情况我们可以通过加锁进行解决,go语言中给我们通过了这个方法
package main
import (
"fmt"
"sync"
)
var lock = sync.Mutex{}
var w = sync.WaitGroup{}
var num = 0
func AddNum() {
lock.Lock() // 上锁
for i := 0; i < 1000000; i++ {
num++
}
lock.Unlock() // 解锁
w.Done()
}
func main() {
w.Add(2)
go AddNum()
go AddNum()
w.Wait()
fmt.Println(num)
}