导致上下文被取消的事件序列

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

Sequence of events leading up to cancellation of context

问题

在下面的程序中,C1C2中都有一个选择序列。在C1中定义了一个1.5秒的截止时间上下文,超过截止时间后,取消上下文,导致ctx.Done(),阻止了在C2中读取整数的操作,进而导致C1中的ctx.Done()

如果是这种情况,那么在上下文已经被取消的情况下,C1中的cancelCtx()有什么用处呢?

package main

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

func main() {
	ctx := context.Background()
	C1(ctx)
}

func C1(ctx context.Context) {
	deadline := time.Now().Add(1500 * time.Millisecond)
	ctx, cancelCtx := context.WithDeadline(ctx, deadline)
	printCh := make(chan int)
	go C2(ctx, printCh)
	for num := 1; num <= 3; num++ {
		select {
		case printCh <- num:
			time.Sleep(1 * time.Second)
		case <-ctx.Done():
			fmt.Printf("C1: Finished\n")
		}
	}
	cancelCtx()
	time.Sleep(100 * time.Millisecond)
}

func C2(ctx context.Context, printCh <-chan int) {
	for {
		select {
		case num := <-printCh:
			fmt.Printf("C2: %d\n", num)
		case <-ctx.Done():
			if err := ctx.Err(); err != nil {
				fmt.Printf("C2 Error: %s\n", err)
			}
			fmt.Printf("C2: Finished\n")
			return
		}
	}
}
英文:

In the below program, there is a select sequence in both C1 & C2. A deadline context for 1.5 seconds is defined in C1, which after the deadline, cancels the context, leads to ctx.Done() and prevents the reading of integers in C2 which would further cancel the context and lead to ctx.Done() in C1.

If that is the case then what use is cancelCtx() in C1 when the context is already being cancelled?

package main

import (
	&quot;context&quot;
	&quot;fmt&quot;
	&quot;time&quot;
)

func main() {
	ctx := context.Background()
	C1(ctx)
}
func C1(ctx context.Context) {
	deadline := time.Now().Add(1500 * time.Millisecond) 
	ctx, cancelCtx := context.WithDeadline(ctx, deadline)
	printCh := make(chan int) 
	go C2(ctx, printCh)
	for num := 1; num &lt;= 3; num++ {
		select {
		case printCh &lt;- num:
			time.Sleep(1 * time.Second)
		case &lt;-ctx.Done():
			fmt.Printf(&quot;C1: Finished\n&quot;)
		}
	}
	cancelCtx()
	time.Sleep(100 * time.Millisecond)
}
func C2(ctx context.Context, printCh &lt;-chan int) {
	for {
		select {
		case num := &lt;-printCh:
			fmt.Printf(&quot;C2: %d\n&quot;, num)
		case &lt;-ctx.Done():
			if err := ctx.Err(); err != nil {
				fmt.Printf(&quot;C2 Error: %s\n&quot;, err)
			}
			fmt.Printf(&quot;C2: Finished\n&quot;)
			return
		}
	}
}

答案1

得分: 2

根据context.WithDeadline()的文档:

取消此上下文会释放与其关联的资源,因此代码应在此上下文中的操作完成后尽快调用cancel。

因此,最佳实践是在不再需要上下文时尽快调用cancelCtx()(或者您称之为的函数),即使这意味着多次调用也是很常见的(参见这里)。

在您的示例中,这可能有另一个好处。假设截止时间是可配置的,并且已增加到5秒;现在取消上下文会导致通过调用cancelCtx()触发C2的终止,而不是等待截止时间(playground)。

英文:

As per the docs for context.WithDeadline():

>Canceling this context releases resources associated with it, so code should call cancel as soon as the operations running in this Context complete.

So best practice is to call cancelCtx() (or whatever you call the function) as soon as you no longer need it (it's fairly common to defer cancel() even if this means it's called multiple times).

In your example there is another reason that this might be beneficial. Lets assume that the deadline is configurable and has been increased to 5 seconds; now cancelling the context leads to the termination of C2 being triggered by the call cancelCtx() instead of waiting until the deadline (playground).

huangapple
  • 本文由 发表于 2022年7月28日 15:24:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/73148806.html
匿名

发表评论

匿名网友

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

确定