如何知道上下文是不是已被取消?

Posted

技术标签:

【中文标题】如何知道上下文是不是已被取消?【英文标题】:How to know if the context has been cancelled?如何知道上下文是否已被取消? 【发布时间】:2021-10-07 13:30:23 【问题描述】:

如何知道上下文是否已被取消?

在下面的示例代码中,

有 2 个任务。 如果这些任务中的任何一个先完成,我想通过上下文取消了解其他任务。 作为示例的一部分,task2 将始终首先完成(在 task1 之前)。
package main

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

func task2(ctx context.Context, ch chan int) 
    for i := 0; i <= 10; i++ 
        if ctx.Err() != nil 
            // In case task1 completes first,
            // I want to print this error. How do I reach this block?
            fmt.Println("Cancelled 2", ctx.Err())
        

        fmt.Println("Task2 ===== ", i)
        time.Sleep(1 * time.Second)
        if i == 2 
            ch <- 2
        
    


func task1(ctx context.Context, ch chan int) 
    for i := 0; i <= 10; i++ 
        if ctx.Err() != nil 
            // In case task2 completes first,
            // I want to print this error. How do I reach this block?
            fmt.Println("Cancelled 1", ctx.Err())
        

        fmt.Println("Task1 ----- ", i)
        time.Sleep(1 * time.Second)
        if i == 5 
            ch <- 1
        
    


func main() 
    ctx, cancel := context.WithCancel(context.Background())
    ch := make(chan int)

    go task1(ctx, ch)
    go task2(ctx, ch)
    
    d := <-ch
    cancel() // If either of task1 or task2 completes, invoke the cancel() func

    fmt.Println("Task which completed first: ", d)

【问题讨论】:

【参考方案1】:

由于您的频道没有缓冲,task1 将在 ch &lt;- 1 中死锁,因为您在 main() 中的 ch 上只有一次阅读。要解决此问题,您可以将其转换为检查 ctx.Done() 的选择语句:

if i == 5 
    select 
        case ch <- 1:
            return
        case <-ctx.Done():
            fmt.Println("Cancelled 1", ctx.Err())
            return
    

您可以在task2 中执行相同操作。

请注意,当main() 终止时,任何仍然存在的 go 例程都会随之终止,无论它们在做什么。如果您不希望这样,则需要提供额外的同步,例如使用 sync.WaitGroup,如下所示:

func a(wg *sync.WaitGroup) 
    defer wg.Done()
    time.Sleep(time.Second)
    fmt.Println("a is done!")


func b(wg *sync.WaitGroup) 
    defer wg.Done()
    time.Sleep(time.Second*2)
    fmt.Println("b is done, too!")


func main() 
    wg := &sync.WaitGroup
    wg.Add(2)

    go a(wg)
    go b(wg)

    wg.Wait()

    fmt.Println("Everyone is done! We can terminate without interrupting anyone.")

【讨论】:

以上是关于如何知道上下文是不是已被取消?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 golang 上下文功能而不是 golang http 客户端发出 http 请求?

没有取消传播的上下文

电脑Google浏览器该网页已被禁止怎么办?

上下文因超时而取消但计算未中止?

警告:上下文初始化期间遇到异常 - 取消刷新尝试

如何判断 OpenGL 上下文是不是是硬件加速的?