在golang中,即使将上下文传递给函数,也不会执行ctx.Done。

huangapple go评论96阅读模式
英文:

context ctx.Done not being executed even though context was passed to the function in golang

问题

我只会翻译你提供的文本,以下是翻译的结果:

我只是不明白为什么即使我传递了上下文并从主函数调用了取消函数,ctx.Done() 仍然没有被执行。我在这里做错了什么?

var c = make(chan string)

func A(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println("killing AAAA")
			return // 至少杀死 A
		default:
			fmt.Println("in A1.. .. again")
			c <- "yesss"
		}
	}
}

//func B(ctx context.Context) {

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	fmt.Println("BEFORE Number of active goroutines ", runtime.NumGoroutine())
	go A(ctx)
	time.Sleep(2 * time.Second)
	valueReceived := <-c
	cancel()
	fmt.Println("AFTER Number of active goroutines ", runtime.NumGoroutine())
}

我希望这可以帮助到你。

英文:

I just don't understand why ctx.Done() is not being executed even though I am passing context and calling the cancel from the main? What am I doing wrong here?

var c = make(chan string)

func A(ctx context.Context) {
 for {
 	select {
 	case &lt;-ctx.Done():
 		fmt.Println(&quot;killing AAAA&quot;)
 		return // kill A at least
 	default:
		fmt.Println(&quot;in A1.. .. again&quot;)
 		c &lt;- &quot;yesss&quot;
	}
 }
}

//func B(ctx context.Context) {

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	fmt.Println(&quot;BEFORE Number of active goroutines &quot;, runtime.NumGoroutine())
	go A(ctx)
	time.Sleep(2 * time.Second)
	valueReceived := &lt;-c
	cancel()
	fmt.Println(&quot;AFTER Number of active goroutines &quot;, runtime.NumGoroutine())
}

答案1

得分: 4

goroutine执行了默认分支两次,并在发送到c时阻塞。&lt;-ctx.Done()分支没有执行,因为goroutine被卡在了默认分支中。

通过从select语句中发送消息而不是分支语句中发送消息来修复这个问题。

func A(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("killing AAAA")
            return // 至少杀死A
        case c <- "yesss":
            fmt.Println("in A1.. .. again")
        }
    }
}

仅通过这个改变可能看不到killing AAAA的输出,因为程序可能在goroutine执行完之前就退出了。

等待goroutine执行完毕以查看消息:

var wg sync.WaitGroup

func A(ctx context.Context) {
    defer wg.Done()
    for {
        select {
        case <-ctx.Done():
            fmt.Println("killing AAAA")
            return // 至少杀死A
        case c <- "yesss":
            fmt.Println("in A1.. .. again")
        }
    }
}

...

wg.Add(1)
go A(ctx)
time.Sleep(2 * time.Second)
valueReceived := <-c
cancel()
wg.Wait()

在Go Playground上运行

英文:

The goroutine executes the default branch twice and blocks on send to c. The &lt;-ctx.Done() case is not executed because the goroutine is stuck in the default branch.

Fix the problem by sending from the select case instead of the branch statements.

func A(ctx context.Context) {
	for {
		select {
		case &lt;-ctx.Done():
			fmt.Println(&quot;killing AAAA&quot;)
			return // kill A at least
		case c &lt;- &quot;yesss&quot;:
			fmt.Println(&quot;in A1.. .. again&quot;)
		}
	}
}

You may not see the the killing AAAA with this change alone because the program can exit before the goroutine runs to completion.

Wait for the goroutine to complete to see the message:

var wg sync.WaitGroup

func A(ctx context.Context) {
	defer wg.Done()
	for {
		select {
		case &lt;-ctx.Done():
			fmt.Println(&quot;killing AAAA&quot;)
			return // kill A at least
		case c &lt;- &quot;yesss&quot;:
			fmt.Println(&quot;in A1.. .. again&quot;)
		}
	}
}

...

wg.Add(1)
go A(ctx)
time.Sleep(2 * time.Second)
valueReceived := &lt;-c
cancel()
wg.Wait()

Run it on the Go playground.

huangapple
  • 本文由 发表于 2021年5月20日 01:22:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/67608032.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定