Go语言中Goroutine如何退出

在Go语言中,启动一个协程Goroutine很方便,一行代码的事儿,比如go runTask()。但是如果想要在这个goroutine的外部,来关闭这个goroutine呢?

举个例子,在web请求A中,启动了一个goroutine来运行某个任务,就像这样 go runTask()。它的内部是一个for循环,循环获取数据,同时检查任务的运行状态。如果不再是运行状态了,就可以退出了。

还有请求B,我们希望通过调用它来关闭请求A中启动的goroutine,要如何操作呢?通过channel发消息,那么这个channel要怎么在两个请求之间使用呢?使用全局的channel即可方便在请求中访问使用了。

这里会有一个问题,即每次请求A都将启动一个channel,在请求B中如何关闭一个特定的goroutine呢?答案自然是map,每个channel和运行的任务id映射起来即可。每次通过取出特定的channel,然后关闭它。

此外,在看过很多关于goroutine的优雅退出文章后,我们将采用context来退出,因为context多次关闭也不会panic,而普通的channel是不允许关闭两次否则panic。Go文档里的示例是这样的:

func Stream(ctx context.Context, out chan<- Value) error {
	for {
		v, err := DoSomething(ctx)
		if err != nil {
			return err
		}
		select {
		case <-ctx.Done():
			return ctx.Err()
		case out <- v:
		}
	}
}

要有一个context实例。然后先处理业务,之后在业务处理完成后,通过select来接受多个channel的输入,这里就接收了context的Done()事件。

或许眼尖的你已经发现了,这个ctx还传递到了DoSomething的函数里,为啥呢?因为context的关闭事件是一个广播操作,其实就是channel的关闭,会导致所有监听在此channel上的收到消息,那么通过传递ctx到下层的DoSomething函数里,在ctx关闭时候,DoSomething也会响应这个关闭动作。

接下里我们看看实际的操作吧。

// 存储context的对象
type CtxUtils struct {
	Cancel context.CancelFunc
	Ctx context.Context
}

// map
var TasksCtx = make(map[uint64]*utils.CtxUtils)

在web请求中,检测该任务是否已经有运行中的goroutine,有则删除并且取消。其中context.WithCancel()是一个可以取消运行的context。还有WithDeadline的context,它是可以设置超时时间的context。只要我们调用这个cancel函数,以下监听的ctx.Done()的都会收到消息,我们就可以退出了。

if prevTaskCtx, ok := TasksCtx[taskId]; ok {
	delete(TasksCtx, taskId)
	prevTaskCtx.Cancel()
}
ctx, cancel := context.WithCancel(context.Background())
TasksCtx[taskId] = &utils.CtxUtils{
	Ctx: ctx,
	Cancel: cancel,
}
go XxxMgr.runTaskWorker(taskId, ctx)

对应的runTaskWorker里,我们将ctx传递过去了。

func (cm *XxManager) runTaskWorker(taskId uint64, ctx context.Context) {
	defer func() {
		utils.Sugar.Infof("task %d exit", taskId)
		if _, ok := TasksCtx[taskId]; ok {
			delete(TasksCtx, taskId)
		}
	}()
	
	for {
		select {
		case <- ctx.Done():
			utils.Sugar.Infof("Goroutine [Task: %d] Exit: run task worker", taskId)
			return
		default:
			doSomethings(ctx)
			// or
			time.Sleep(10 * time.Second)
	}
}

看过很多人写的channel优雅退出,奈何自己第一次在实际业务中写出来,还是很开心的。。。哈哈哈

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值