package main
import (
"context"
"fmt"
"sync"
"time"
)
func myfunc(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Printf("Ctx is kicking in with error:%+v\n", ctx.Err())
return
default:
time.Sleep(15 * time.Second)
fmt.Printf("I was not canceled\n")
return
}
}
}
func main() {
ctx, cancel := context.WithTimeout(
context.Background(),
time.Duration(3*time.Second))
defer cancel()
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
myfunc(ctx)
}()
wg.Wait()
fmt.Printf("In main, ctx err is %+v\n", ctx.Err())
}
Run Code Online (Sandbox Code Playgroud)
I have the above snippet that does print the output like this
I was not canceled
In main, ctx err is context deadline exceeded
Process finished with exit code 0
Run Code Online (Sandbox Code Playgroud)
I understand that context times-out after 3 seconds and hence it does give me the expected error when I call ctx.Err() in the end. I also get the fact that in my myfunc once select matches on the case for default, it won't match on the done. What I do not understand is that how do I make my go func myfunc get aborted in 3 seconds using the context logic. Basically, it won't terminate in 3 seconds so I am trying to understand how can golang's ctx help me with this?
nov*_*ung 20
如果你要使用的超时和取消功能从上下文,然后在你的情况的ctx.Done()需要进行同步处理。
来自https://golang.org/pkg/context/#Context 的解释
Done 返回一个在代表此上下文完成工作时关闭的通道应该被取消。如果此上下文永远无法取消,则 Done 可能返回 nil。对 Done 的连续调用返回相同的值。
所以基本上<-ctx.Done()将在两个条件下调用:
当这种情况发生时,ctx.Err()将永远不会nil。
我们可以对错误对象进行一些检查,以查看上下文是否被强制取消或超过超时。
Context 包提供了两个错误对象,context.DeadlineExceeded以及context.Timeout,这两个将帮助我们识别为什么<-ctx.Done()被调用。
cancel())在测试中,我们将尝试在超时之前取消上下文,以便<-ctx.Done()执行。
ctx, cancel := context.WithTimeout(
context.Background(),
time.Duration(3*time.Second))
go func(ctx context.Context) {
// simulate a process that takes 2 second to complete
time.Sleep(2 * time.Second)
// cancel context by force, assuming the whole process is complete
cancel()
}(ctx)
select {
case <-ctx.Done():
switch ctx.Err() {
case context.DeadlineExceeded:
fmt.Println("context timeout exceeded")
case context.Canceled:
fmt.Println("context cancelled by force. whole process is complete")
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
$ go run test.go
context cancelled by force
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我们使进程花费的时间比上下文超时长,因此理想情况下<-ctx.Done()也将执行。
ctx, cancel := context.WithTimeout(
context.Background(),
time.Duration(3*time.Second))
go func(ctx context.Context) {
// simulate a process that takes 4 second to complete
time.Sleep(4 * time.Second)
// cancel context by force, assuming the whole process is complete
cancel()
}(ctx)
select {
case <-ctx.Done():
switch ctx.Err() {
case context.DeadlineExceeded:
fmt.Println("context timeout exceeded")
case context.Canceled:
fmt.Println("context cancelled by force. whole process is complete")
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
$ go run test.go
context timeout exceeded
Run Code Online (Sandbox Code Playgroud)
可能会出现这样的情况,因为发生了错误,我们需要在过程中停止 goroutine。有时,我们可能需要在主程序中检索该错误对象。
为了实现这一点,我们需要一个额外的通道来将错误对象从 goroutine 传输到主程序中。
在下面的示例中,我准备了一个名为chErr. 每当(goroutine)进程中间发生错误时,我们将通过通道发送该错误对象,然后立即停止进程。
ctx, cancel := context.WithTimeout(
context.Background(),
time.Duration(3*time.Second))
chErr := make(chan error)
go func(ctx context.Context) {
// ... some process ...
if err != nil {
// cancel context by force, an error occurred
chErr <- err
return
}
// ... some other process ...
// cancel context by force, assuming the whole process is complete
cancel()
}(ctx)
select {
case <-ctx.Done():
switch ctx.Err() {
case context.DeadlineExceeded:
fmt.Println("context timeout exceeded")
case context.Canceled:
fmt.Println("context cancelled by force. whole process is complete")
}
case err := <-chErr:
fmt.Println("process fail causing by some error:", err.Error())
}
Run Code Online (Sandbox Code Playgroud)
cancel()在上下文初始化后立即调用根据有关该功能的上下文文档cancel():
取消此上下文会释放与其关联的资源,因此一旦此上下文中运行的操作完成,代码应立即调用取消。
总是cancel()在上下文声明之后立即调用函数是很好的。它是否也在 goroutine 中被调用并不重要。这是因为当块内的整个过程完全完成时,确保上下文总是被取消。
ctx, cancel := context.WithTimeout(
context.Background(),
time.Duration(3*time.Second))
defer cancel()
// ...
Run Code Online (Sandbox Code Playgroud)
defer cancel()在 goroutine 中调用您可以在 goroutine 中defer的cancel()语句上使用(如果需要)。
// ...
go func(ctx context.Context) {
defer cancel()
// ...
}(ctx)
// ...
Run Code Online (Sandbox Code Playgroud)
Dom*_*nes 14
在您的 中for ... select,您有两种情况:case <-ctx.Done():和default:。当您的代码到达 时select,它会进入这种default情况,因为上下文尚未取消,它会休眠 15 秒然后返回,从而中断您的循环。(换句话说,它不会阻止/等待您的上下文取消)
如果您希望您的代码执行您所描述的操作,您需要select有上下文被取消和强制超时的情况。
select {
case <-ctx.Done(): // context was cancelled
fmt.Printf("Ctx is kicking in with error:%+v\n", ctx.Err())
return
case <-time.After(15 * time.Second): // 15 seconds have elapsed
fmt.Printf("I was not canceled\n")
return
}
Run Code Online (Sandbox Code Playgroud)
现在,您的代码将在遇到 时阻塞select,而不是进入default案例并打破循环。