多个生产者,单个消费者:所有 goroutine 都在睡觉 - 死锁

2024-06-02

在继续工作之前,我一直遵循检查通道中是否有任何内容的模式:

func consume(msg <-chan message) {
  for {
    if m, ok := <-msg; ok {
      fmt.Println("More messages:", m)
    } else {
      break
    }
  }
}

就是基于这个video https://youtu.be/YS4e4q9oBaU?t=23240。这是我的完整代码:

package main

import (
    "fmt"
    "strconv"
    "strings"
    "sync"
)

type message struct {
    body string
    code int
}

var markets []string = []string{"BTC", "ETH", "LTC"}

// produces messages into the chan
func produce(n int, market string, msg chan<- message, wg *sync.WaitGroup) {
    // for i := 0; i < n; i++ {
    var msgToSend = message{
        body: strings.Join([]string{"market: ", market, ", #", strconv.Itoa(1)}, ""),
        code: 1,
    }
    fmt.Println("Producing:", msgToSend)
    msg <- msgToSend
    // }
    wg.Done()
}

func receive(msg <-chan message, wg *sync.WaitGroup) {
    for {
        if m, ok := <-msg; ok {
            fmt.Println("Received:", m)
        } else {
            fmt.Println("Breaking from receiving")
            break
        }
    }
    wg.Done()
}

func main() {
    wg := sync.WaitGroup{}
    msgC := make(chan message, 100)
    defer func() {
        close(msgC)
    }()
    for ix, market := range markets {
        wg.Add(1)
        go produce(ix+1, market, msgC, &wg)
    }
    wg.Add(1)
    go receive(msgC, &wg)
    wg.Wait()
}

如果你尝试运行它,我们会在打印即将打破的消息之前陷入僵局。说实话,这是有道理的,因为上次,当 chan 中没有其他内容时,我们试图提取该值,所以我们得到了这个错误。但这种模式是行不通的if m, ok := <- msg; ok。如何使此代码工作以及为什么会出现此死锁错误(大概此模式应该有效?)。


考虑到您在单个通道上确实有多个写入器,您会遇到一些挑战,因为在 Go 中执行此操作的简单方法通常是在单个通道上有一个写入器,然后让该单个写入器关闭发送最后一个数据时的通道:

func produce(... args including channel) {
    defer close(ch)
    for stuff_to_produce {
        ch <- item
    }
}

这种模式有一个很好的特性,无论你如何摆脱produce,通道关闭,表明生产结束。

您没有使用这种模式——您将一个通道传递给许多 goroutine,每个 goroutine 都可以发送one消息——所以你需要移动close(或者,当然,使用其他一些模式)。表达所需模式的最简单方法是:

func overall_produce(... args including channel ...) {
    var pg sync.WaitGroup
    defer close(ch)
    for stuff_to_produce {
        pg.Add(1)
        go produceInParallel(ch, &pg) // add more args if appropriate
    }
    pg.Wait()
}

The pg计数器累计活跃生产者。每个人都必须打电话pg.Done()表明它是使用完成的ch。总体制作人现在等待它们全部完成,然后it在退出时关闭通道。

(如果你写内部produceInParallel函数作为一个闭包,你不需要通过ch and pg明确地向它表达。你也可以写overallProducer作为闭包。)

请注意,您的单个消费者的循环可能最好使用for ... range构造:

func receive(msg <-chan message, wg *sync.WaitGroup) {
    for m := range msg {
        fmt.Println("Received:", m)
    }
    wg.Done()
}

(你提到了添加一个select到循环,以便在消息尚未准备好时可以执行其他计算。如果该代码无法分离成独立的 goroutine,那么您实际上需要更高级的m, ok := <-msg构造。)

另请注意,wg for receive——这可能是不必要的,取决于你如何构建其他事物——完全独立于等待组pg对于生产者来说。虽然确实如所写的,在所有生产者完成之前消费者无法完成,但我们希望独立等待生产者完成,以便我们可以关闭整体生产者包装器中的通道。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多个生产者,单个消费者:所有 goroutine 都在睡觉 - 死锁 的相关文章

随机推荐

  • 如何指定 AsRef 的生命周期?

    我正在尝试编写一个函数来连接两个可迭代对象 其项目可以转换为OsStr引用 并且在尝试指定引用的生命周期时遇到了巨大的困难 use std convert AsRef use std ffi OsStr use std marker Pha
  • 使用 matplotlib 同时绘制两个直方图时,不透明度会产生误导

    假设我有两个直方图 我使用 hist 参数设置不透明度 alpha 0 5 我绘制了两个直方图 但我得到了三种颜色 我理解从不透明的角度来看这是有道理的 但 向某人展示具有三种颜色的两个事物的图表会让人非常困惑 我可以以某种方式将每个垃圾箱
  • 为什么 Sleep() 会使后续代码减慢 40 毫秒?

    我最初是在 coderanch com 上询问这个问题的 所以如果您尝试在那里帮助我 谢谢 并且不必重复这个努力 不过 coderanch com 主要是一个 Java 社区 而且 经过一些研究 这似乎确实是一个 Windows 问题 因此
  • 如何为活动添加背景图片?

    使用主题或 ImageView use the android backgroundxml 中的属性 如果您想将其应用到整个活动 最简单的方法是将其放在布局的根目录中 因此 如果您有一个relativelayout作为您的xml的开头 请将
  • 执行命令后如何保持 sbt 运行(作为守护进程)

    我想从 sbt 启动 scalatra 服务器 我怎么做 以下命令启动 scalatra sbt container start 但它立即退出 info starting server success Total time 2 s comp
  • vim 的 @@ 变量是什么?

    我知道通过输入 我执行最后一个 命令 但谁能解释一下什么 是在下面的代码中 在 vim 帮助文件中找到 function CountSpaces type let sel save selection let reg save if a 0
  • SQL Server 中的派生表

    我有这两个疑问 我不知道如何将它们组合在一起来制作派生表 我假设使用第二个查询作为主查询 并在主查询的 FROM 子句中使用第一个查询 SELECT EmailAddress Orders OrderID SUM ItemPrice Qua
  • mongodb 中的 Redact 对我来说似乎很晦涩

    我现在正在与 redact 作斗争 我不确定是否理解它 我刚刚阅读了文档并尝试在集合成绩上使用 redact 它来自 mongodb 在线培训 grades 集合中的文档如下所示 id ObjectId 50b59cd75bed76f465
  • 计算 Int32 中的前导零

    如何计算一个数组中的前导零Int32 所以我想做的是写一个函数 如果我的输入是 2 它返回 30 因为在二进制中我有000 0000000000010 NOTE使用 dotnet core gt 3 0 看here https stacko
  • 使用 axios 发送文件,不使用 FormData api

    我可以使用 axios 和 FormData api 将文件发送到服务器 如下所示 persist avatar let data new FormData data append avatar avatar axios post api
  • Qt ObjectName() 必须是唯一的吗?

    如标题所示的简单问题 如果我打电话setObjectName 在一个对象上 它是否必须是唯一的 或者只是因为约定而推荐 我已经子类化了QLabel 并希望自动为创建的对象命名 如果这是一个坏主意 我会找到一些设置随机唯一名称的方法 我实际上
  • 如何比 CGContextStrokePath 更快地渲染线条?

    我正在使用 CGContextStrokePath 绘制约 768 个点的图表 问题是 每一秒我都会得到一个新的数据点 从而重新绘制图表 目前 这个已经很繁忙的应用程序占用了 50 的 CPU 图形绘制是在UIView 中的drawRect
  • Phantomjs - 如何填充表单、提交并获取结果?

    我似乎无法进行简单的表单提交 下面是我向 Google 搜索表单提交 测试 并打印结果的代码 var url http www google com page new WebPage page open url function statu
  • 如何从脚本/模块 __main__ 启动 Celery Worker?

    我定义了一个Celery应用程序在一个模块中 现在我想从其同一模块中启动工作程序 main 即通过运行模块python m代替celery从命令行 我试过这个 app Celery project include project tasks
  • DeviceOrientationEvent:当 beta 接近/达到 90 度时如何处理疯狂的伽玛?

    有人有 DeviceOrientationEvent 经验并且有手机 平板电脑吗 在具有陀螺仪的设备上运行以下代码片段 我注意到当 beta 接近 90 度 设备指向上方 时 gamma 沿 y 轴左 右旋转 变得很大且不可预测 我假设这是
  • Django:无法为用于检索数据的模型实例化抽象模型

    我正在开发一个项目 该项目有一个 Djongo 抽象模型和一个主模型 当我尝试插入一个值时 它被插入而没有错误 但是当我尝试检索数据时 我得到 抽象模型无法实例化 这是我的模型 class Exam questions models Mod
  • 如何向javascript的日期时间对象添加24小时[重复]

    这个问题在这里已经有答案了 可能的重复 向 Javascript Date 对象添加小时 https stackoverflow com questions 1050720 adding hours to javascript date o
  • 使用 python 字典更新 MongoEngine 文档?

    是否可以使用 python 字典更新 MongoEngine 文档 例如 class Pets EmbeddedDocument name StringField class Person Document name StringField
  • ValueError:数学域错误,不断弹出

    我时常收到此消息 我尝试了所有的变化 改变我使用 sqrt 的方式 一步一步地做 等等 但这个错误仍然不断出现 这可能是一个菜鸟错误 我没有注意到 因为我是 python 和 ubuntu 的新手 这是我的源代码 一个非常简单的程序 To
  • 多个生产者,单个消费者:所有 goroutine 都在睡觉 - 死锁

    在继续工作之前 我一直遵循检查通道中是否有任何内容的模式 func consume msg lt chan message for if m ok lt msg ok fmt Println More messages m else bre