1

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

在以下示例代码中,

  • 有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)
}
4

1 回答 1

5

由于您的频道是无缓冲的,task1因此会死锁,ch <- 1因为您只有一次阅读chin main()。要解决此问题,您可以将其转换为您检查的选择语句ctx.Done()

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

您可以在task2.

请注意,当main()终止时,任何仍然存在的 goroutine 都会随之终止,无论它们在做什么。如果您不希望这样,您需要提供额外的同步,例如使用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.")
}
于 2021-08-02T07:11:14.173 回答