假设出现以下情况:

  • 我们有下面的Consumer函数,运行在Goroutine中.

  • 另一个Goroutine正在毫无延迟地在intChan通道上发送整数.换句话说,在for循环的每一次迭代中,都有一个值准备在intChan上接收.

  • 启动Consumer号大猩猩的大猩猩,取消了进入Consumer号大猩猩的背景.因此,ctx.Done()通道也有一个可供接收的值.

问题:

  • 在这种情况下,SELECT语句的两种情况都可以运行.
  • 根据tour of Go,select将随机挑选一个病例,因为两个都已经准备好运行.
  • 如何保证select个人不会一直 Select <- intChan个 case ?如果这两个 case 在for循环的每个迭代中都准备好了,我们如何知道<- ctx.Done() case 最终会被选中?
func Consumer(ctx context.Context, intChan chan int) {
    for {
        select {
        case <-ctx.Done():
            return
        case i := <-intChan:
            foo(i)
        }
    }
}

在下面的程序中,我try 使用了Consumer函数. 在这个程序的几次运行中,ConsumerProducer个Goroutine似乎总是终止.

为什么我们不以运行<-ctx.Done()个 case 的方式结束呢?

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:
        }
    }
}

推荐答案

这是没有保证的.保证终止的最简单方法是在SELECT语句外部判断错误(ctx.Err()).将错误返回给传递上下文的代码也很常见.我会这样写消费者基金:

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

Go相关问答推荐

如何将文件从AWS S3存储桶复制到Azure BLOB存储

如何在Golang中获取mp3文件的持续时间?

Hugo错误:没有为此项目配置现有内容目录

从文件读取字节,将其保存到 struct 体并修改值

如何使用 AWS sdk 在 Go 中正确解组 PartiQL 查询的结果?

Go 中将 int 切片转换为自定义 int 切片指针类型的函数

Kafka golang 生产者在错误后更改分区计数

在golang二进制中嵌入SvelteKit

Go Programming Language书上的例子server2错了吗?

xml.Unmarshal 不支持的类型 struct

具有嵌套重复的正则表达式

读取非UTF8编码的文件内容并正确打印出来

查找、解析和验证邮箱地址

合并几千万文件最快的方法是什么

关系不存在 GORM

Golang:每个键具有多个值的映射

Go:如何创建一个可以提供配置文件中描述的 url 的服务器

如何使用 fyne 避免 GUI 应用程序中的循环依赖?

有没有办法停止long blocking 函数?

如果在调用 http.Get(url) 时发生错误,我们是否需要关闭响应对象?