背景
上次基于mysql实现分布式锁,今天经过测试发现问题,主要是协程不断获取锁的逻辑存在问题,因为获取锁的协程挂掉之后,但其新生成的用来不断更新锁的协程并不会退出,导致锁一直不能被释放,究其原因如下
原因
通过下面代码即可说明
fmt.Println("main 函数 开始...")
go func() {
fmt.Println("父 协程 开始...")
go func() {
for {
fmt.Println("子 协程 执行中...")
timer := time.NewTimer(time.Second * 2)
<-timer.C
}
}()
time.Sleep(time.Second*5)
fmt.Println("父 协程 退出...")
}()
time.Sleep(time.Second*10)
fmt.Println("main 函数 退出")
main 函数 开始...
父 协程 开始...
子 协程 执行中...
子 协程 执行中...
子 协程 执行中...
父 协程 退出...
子 协程 执行中...
子 协程 执行中...
main 函数 退出
由此可以看出:
- main 函数退出,所有协程退出
- 协程无父子关系,即在父协程开启新的协程,若父协程退出,不影响子协程
解决方式
通过context上下文来解决,当然也可以通过channel管道来解决,context解决方式如下:
fmt.Println("main 函数 开始...")
go func() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
fmt.Println("父 协程 开始...")
go func(ctx context.Context) {
for {
for {
select {
case <-ctx.Done():
fmt.Println("子 协程 接受停止信号...")
return
default:
fmt.Println("子 协程 执行中...")
timer := time.NewTimer(time.Second * 2)
<-timer.C
}
}
}
}(ctx)
time.Sleep(time.Second*5)
fmt.Println("父 协程 退出...")
}()
time.Sleep(time.Second*10)
fmt.Println("main 函数 退出")
main 函数 开始...
父 协程 开始...
子 协程 执行中...
子 协程 执行中...
子 协程 执行中...
父 协程 退出...
子 协程 接受停止信号...
main 函数 退出
写在最后
我司专业专注各种服务器代理:
1 来自于阿里、腾讯等大厂开发人员免费答疑,针对您的使用场景规划最合理产品方案和架构,最大限度节约成本。
2.各类云服务器全网超低价。 联想、华为、阿里、腾讯等厂商指定合作代理公司,购买有保障。
以远低于原厂的价格享受原厂的服务!