在Go中使用取消上下文在循环中启动goroutine很奇怪。

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

Strange in Go start groutine in loop with cancel context

问题

我的情况:处理器有三次重试,每次重试花费一秒钟,处理器不能超时,所以我使用CancelContext来控制它。

我的处理器函数:

func process(ctx context.Context, ch chan<- int) {
	fmt.Println("process execute")
	select {
	case <-ctx.Done():
		fmt.Println("process receive done signal, return")
		return
	default:
		fmt.Println("execute default")
		time.Sleep(4 * time.Second) // 处理一些事情...
		ch <- 1
		return
	}
}

情况1

func main(){
	ctx, cancel := context.WithCancel(context.Background()) // 处理超时

	for i := 0; i < 3; i++ { // 重试3次
		flag := make(chan int, 1)
		go process(ctx, flag)
		select {
		case <-flag:
			fmt.Println("main receive task done")
		case <-time.After(time.Second * 3):
			cancel()
			fmt.Println("main receive timeout")
		}
	}
}

输出:

process execute
execute default
main receive timeout
process execute
process receive done signal, return
main receive timeout
process execute
process receive done signal, return
main receive timeout

**问题:**奇怪的是,为什么execute default只在第一次循环中打印?

情况2
我在循环中定义了上下文:

func main(){
	for i := 0; i < 3; i++ {
		ctx, cancel := context.WithCancel(context.Background()) // 在循环中定义

		flag := make(chan int, 1)
		go process(ctx, flag)
		select {
		case <-flag:
			fmt.Println("main receive task done")
		case <-time.After(time.Second * 3):
			cancel()
			fmt.Println("main receive timeout")
		}
	}
}

输出:

process execute
execute default
main receive timeout
process execute
execute default
main receive timeout
process execute
execute default
main receive timeout

**问题:**为什么process函数从未打印process receive done signal, return,它没有接收到ctx.Done()信号吗?

对不起,我的英语不好,非常感谢。

英文:

my situation: processor have three retry, and spend second peer retry, and processor cannot be timeout, so i use the CancelContext to controll it.

my processor function:

func process(ctx context.Context, ch chan&lt;- int) {
	fmt.Println(&quot;process execute&quot;)
	select {
	case &lt;-ctx.Done():
		fmt.Println(&quot;process receive done signal, return&quot;)
		return
	default:
		fmt.Println(&quot;execute default&quot;)
		time.Sleep(4 * time.Second) // something handle...
		ch &lt;- 1
		return
	}
}

situation 1

func main(){
	ctx, cancel := context.WithCancel(context.Background()) // handle timeout

	for i := 0; i &lt; 3; i++ { // retry 3
		flag := make(chan int, 1)
		go process(ctx, flag)
		select {
		case &lt;-flag:
			fmt.Println(&quot;main receive task done&quot;)
		case &lt;-time.After(time.Second * 3):
			cancel()
			fmt.Println(&quot;main receive timeout &quot;)
		}
	}
}

output:

process execute
execute default
main receive timeout 
process execute
process receive done signal, return
main receive timeout 
process execute
process receive done signal, return
main receive timeout

question: somethine strange happend, why execute default only print in first loop?

situation 2
i define context in loop:

func main(){
	for i := 0; i &lt; 3; i++ {
		ctx, cancel := context.WithCancel(context.Background()) // define in loop

		flag := make(chan int, 1)
		go process(ctx, flag)
		select {
		case &lt;-flag:
			fmt.Println(&quot;main receive task done&quot;)
		case &lt;-time.After(time.Second * 3):
			cancel()
			fmt.Println(&quot;main receive timeout &quot;)
		}
	}
}

output:

process execute
execute default
main receive timeout 
process execute
execute default
main receive timeout 
process execute
execute default
main receive timeout 

question: why process function never print process receive done signal, return, does it not receive the ctx.Done() singal ?

sorry for my bad english, thank you very much

答案1

得分: 1

有些奇怪的事情发生了,为什么只有在第一个循环中执行默认的打印操作?

因为当你到达第二个和第三个进程时,你的共享上下文已经被取消了。

为什么 process 函数从未打印出 process receive done signal 和 return,它没有接收到 ctx.Done() 信号吗?

因为当你调用 process 函数时,它执行了 select 语句,由于上下文尚未完成,select 语句进入了默认情况,并且不会返回。

英文:

> somethine strange happend, why execute default only print in first
> loop?

Because when you reach the second and the third process your shared context is already canceled.

> why process function never print process receive done signal, return,
> does it not receive the ctx.Done() singal ?

Because when you call process you execute select and since your context is not done - select goes to the default case and doesn't come back.

huangapple
  • 本文由 发表于 2022年9月18日 00:52:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/73756797.html
匿名

发表评论

匿名网友

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

确定