背景
上次基于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.各类云服务器全网超低价。 联想、华为、阿里、腾讯等厂商指定合作代理公司,购买有保障。
以远低于原厂的价格享受原厂的服务!
在这里插入图片描述