我们如何保证一个取消的上下文会导致一个goroutine的终止?

2guxujil  于 2023-08-01  发布在  Go
关注(0)|答案(1)|浏览(93)

假设发生以下情况:

  • 我们有下面的Consumer函数,在一个goroutine中运行。
  • 另一个goroutine是在intChan通道上发送整数,没有任何延迟。换句话说,在for循环的每次迭代中,都有一个值准备在intChan上接收。
  • 启动Consumer goroutine的goroutine取消了传入Consumer的context。因此,ctx.Done()信道也具有准备好被接收的值。

问题:

  • 在这种情况下,select语句的两个case都准备好运行了。
  • 根据tour of Goselect将随机选择一个案例,因为两者都准备好运行。
  • 如何保证select不会继续选择<- intChan shell ?如果<- ctx.Done()<- ctx.Done()在for循环的每次迭代中都准备好了,我们怎么知道<- ctx.Done()最终会被选中?
func Consumer(ctx context.Context, intChan chan int) {
    for {
        select {
        case <-ctx.Done():
            return
        case i := <-intChan:
            foo(i)
        }
    }
}

字符串
在下面的程序中,我尝试使用Consumer函数。在这个程序的几次运行中,ConsumerProducer goroutine似乎总是终止。
为什么我们不以永远不执行<-ctx.Done()案例的运行结束呢?

package main

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

func main() {

    ctx, cancelFunc := context.WithCancel(context.Background())

    var wg sync.WaitGroup
    wg.Add(2) // add 2, because we spawn 2 goroutines

    Producer(ctx, &wg)

    fmt.Println(time.Now())
    time.Sleep(time.Second * 5) // cancel the context after 5 seconds

    cancelFunc()
    fmt.Println("CANCELLED")

    wg.Wait() // wait till both producer and consumer goroutines terminate
    fmt.Println(time.Now())

}

func Producer(ctx context.Context, wg *sync.WaitGroup) {
    intChan := make(chan int)

    go Consumer(ctx, intChan, wg)

    go func() {
        defer wg.Done()
        for {
            select {
            case <-ctx.Done():
                return
            case intChan <- 1:
            }
        }
    }()

}

func Consumer(ctx context.Context, intChan chan int, wg *sync.WaitGroup) {
    defer wg.Done()

    for {
        select {
        case <-ctx.Done():
            return
        case _ = <-intChan:
        }
    }
}

sh7euo9m

sh7euo9m1#

没有任何保证。保证终止的最简单方法是在select语句外使用ctx.Err()检查错误。将错误返回给传递上下文的代码也很常见。我会这样写消费者函数:

func Consumer(ctx context.Context, intChan chan int) error {
    for ctx.Err() == nil {
        select {
        case <-ctx.Done():
        case i := <-intChan:
            foo(i)
        }
    }
    return ctx.Err()
}

字符串

相关问题