【问题标题】: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)
}

【问题讨论】:

    标签: go go-context


    【解决方案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.")
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-28
      • 2015-10-18
      • 1970-01-01
      • 2014-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多