超时的goroutine如何回收?

谈到goroutine并发超时控制,一搜很容易看到下面这样的代码。

func TestGoroutine3(t *testing.T) {
	done := make(chan struct{}, 0)
	go func() {
		// do something
		time.Sleep(time.Second)
		fmt.Println("server")
		done <- struct{}{}
		fmt.Println("send done success!")
	}()
	select {
	case <- done:
		fmt.Println("done")
	case <- time.After(time.Millisecond * 100):
		fmt.Println("time out")
	}
}

这段代码很简,通过channel+select来控制超时,如果是一运行完就结束。那么没有问题。

但是!!,如果是服务器类的程序就有问题,会存在goroutine泄漏,如果服务长期不重启,那么一个次请求如果发生了超时就会泄漏goroutine。为什么?因为done这个channel无缓冲,超时后,当子goroutine执行到done<-struct{}{}将会一直阻塞,导致goroutine不会被回收。

网上太多这样类似的代码,都不提这个问题。正确的做法是是什么呢?在超时后,我们需要回收开启的goroutine。

方法一:超时后直接关闭done这个channel,但是这会引发Panic,没关系,加个defer接住就行

func TestGoroutine(t *testing.T) {
	done := make(chan struct{}, 0)
	go func() {
		defer func() {
			err := recover()
			fmt.Println(err)
		}()
		time.Sleep(time.Second)
		fmt.Println("server")
		done <- struct{}{}
		fmt.Println("send done success!")
	}()
	select {
	case <- done:
		fmt.Println("done")
	case <- time.After(time.Millisecond * 100):
		close(done)
		fmt.Println("time out")
	}
	// 停一段时间看超时goroutine是否继续运行
	time.Sleep(time.Second*3)
	//<-done
	//fmt.Println("3 s, done")
}

方法二:使用context,使用context来取消goroutine,其大概作用机制就是引入一个新的channel,假设叫done2,在开启的goroutine中使用select从done中接受值,当超时时,关闭这个channel,接受就不会再阻塞,从而可以正常回收goroutine。使用context或者自己make一个channel都可以,关于context可以看我的另一篇文章,下面给出context的例子,使用channel也可以轻易实现。

func TestGoroutine2(t *testing.T) {
	done := make(chan struct{}, 0)
	ctx, cancelFunc := context.WithCancel(context.Background())
	go func(ctx context.Context) {
		time.Sleep(time.Second)
		fmt.Println("server")
		select {
		case done <- struct{}{}:
		case <-ctx.Done():
		}
		fmt.Println("send done success!")
	}(ctx)
	select {
	case <- done:
		fmt.Println("done")
	case <- time.After(time.Millisecond * 100):
		cancelFunc()
		fmt.Println("time out")
	}
	// 停一段时间看超时goroutine是否继续运行
	time.Sleep(time.Second*3)
	//<-done
	//fmt.Println("3 s, done")
}

方法三(更新):更简单的方法实际上是将channel大小设为1,这样发送就不会阻塞了。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值