从go例程外部停止goroutine

问题描述

我这里有一个go例程,需要在上下文到期时从外部go例程中停止该例程。但是,即使上下文控制的go例程停止运行,go例程也不会在上下文到期时停止并继续运行。

package main

import (
    "context"
    "fmt"
    "time"
)

func main() {
    ctxParent := context.Background()

    ch := make(chan bool)

    d := 5 * time.Second

    ctx,cancel := context.WithTimeout(ctxParent,d)
    defer cancel()
    go doSomething(ctx,ch)

    // go func() {
    select {

    // done
    case _ = <-ch:
        fmt.Println("goroutine finished")
    }

    fmt.Println("waiting 11 seconds on main thread,ending all go routines")

    time.Sleep(11 * time.Second)

    fmt.Println(">>>> END")
}

func doSomething(ctx context.Context,ch chan<- bool) {

    // how to kill this go routine if context expires??
    go func(ctx context.Context) {
        fmt.Println("LOTS OF WORK TIME..")
        for i := 0; i < 1000; i++ {
            time.Sleep(1 * time.Second) // LOTS OF WORK
            fmt.Println(i)
        }

    }(ctx)

    select {
    case _ = <-ctx.Done():
        fmt.Println("*** Go routine timed out in 5 seconds!!! ****")
        ch <- true
        return

    }
}

这将打印(https://play.golang.org/p/L8u51odiHxS

LOTS OF WORK TIME..
0
1
2
3
4
*** Go routine timed out in 5 seconds!!! ****
goroutine finished
waiting 11 seconds on main thread,ending all go routines
5
6
7
8
9
10
11
12
13
14
15
>>>> END

它不应该打印5,6,7,8 ...等。是否有办法杀死这个内部go例程?

解决方法

您必须在goroutine中检查上下文的过期/取消情况:

defaultData
,

就像Burak Serdar所说,您必须检查goroutine中上下文的状态,而不仅仅是在goroutine的开始时:您必须定期在goroutine中进行检查。

go func(ctx context.Context) {
  for i := 0; i < 1000; i++ {
    select {
    case <-ctx.Done():
      return
    default:
      // do some work
    }
  }
}(ctx)

一般来讲,不应从外部杀死goroutine;它必须自己检查并意识到时间到了。