缓冲区为空后关闭“worker”go 例程

2023-11-29

我想要我的 goroutine 工作人员(ProcessToDo()在下面的代码中)等待所有“排队”工作处理完毕后再关闭。

工作例程有一个“待办事项”通道(缓冲),通过该通道将工作发送给它。它有一个“完成”通道来告诉它开始关闭。文档称,如果满足多个选择,通道上的选择将选择一个“伪随机值”...这意味着在所有缓冲工作完成之前将触发关闭(返回)。

在下面的代码示例中,我希望打印所有 20 条消息...

package main

import (
    "time"
    "fmt"
)


func ProcessToDo(done chan struct{}, todo chan string) {
    for {
        select {
        case work, ok := <-todo:
            if !ok {
                fmt.Printf("Shutting down ProcessToDo - todo channel closed!\n")
                return
            }
            fmt.Printf("todo: %q\n", work)
            time.Sleep(100 * time.Millisecond)
        case _, ok := <-done:
            if ok {
                fmt.Printf("Shutting down ProcessToDo - done message received!\n")
            } else {
                fmt.Printf("Shutting down ProcessToDo - done channel closed!\n")
            }
            close(todo)
            return
        }
    }
}

func main() {

    done := make(chan struct{})
    todo := make(chan string, 100)

    go ProcessToDo(done, todo)

    for i := 0; i < 20; i++ {
        todo <- fmt.Sprintf("Message %02d", i)
    }

    fmt.Println("*** all messages queued ***")
    time.Sleep(1 * time.Second)
    close(done)
    time.Sleep(4 * time.Second)
}

done在您的情况下,通道是完全不必要的,因为您可以通过关闭来发出关闭信号todo频道本身。

并使用for range在通道上,它将迭代直到通道关闭并且其缓冲区为空。

你应该有一个done通道,但只是为了让 goroutine 本身可以发出它已完成工作的信号,以便主 goroutine 可以继续或退出。

这个变体与你的相同,更简单并且不需要time.Sleep()调用等待其他 goroutine(无论如何这都太错误且不确定)。尝试一下去游乐场:

func ProcessToDo(done chan struct{}, todo chan string) {
    for work := range todo {
        fmt.Printf("todo: %q\n", work)
        time.Sleep(100 * time.Millisecond)
    }
    fmt.Printf("Shutting down ProcessToDo - todo channel closed!\n")
    done <- struct{}{} // Signal that we processed all jobs
}

func main() {
    done := make(chan struct{})
    todo := make(chan string, 100)

    go ProcessToDo(done, todo)

    for i := 0; i < 20; i++ {
        todo <- fmt.Sprintf("Message %02d", i)
    }

    fmt.Println("*** all messages queued ***")
    close(todo)
    <-done // Wait until the other goroutine finishes all jobs
}

另请注意,工作协程应使用以下方式发出完成信号:defer因此,如果worker以某种意外的方式返回,或者出现恐慌,主goroutine不会陷入等待worker的状态。所以它应该这样开始:

defer func() {
    done <- struct{}{} // Signal that we processed all jobs
}()

您还可以使用sync.WaitGroup将主 goroutine 同步到工作线程(等待它)。事实上,如果您计划使用多个工作协程,那么这比从done渠道。而且用以下命令来表示完成也更简单WaitGroup因为它有一个Done()方法(这是一个函数调用),所以你不需要匿名函数:

defer wg.Done()

See 吉姆博的回答完整的例子WaitGroup.

使用for range如果您想使用多个工作协程,这也是惯用的:通道是同步的,因此您不需要任何额外的代码来同步对todo频道或从中收到的作业。如果你关闭todo频道中的main(),这将正确地向所有工作协程发出信号。当然,所有排队的作业都会被接收并处理一次。

现在采用使用的变体WaitGroup让主 Goroutine 等待工人(JimB 的回答):如果你想要超过 1 个工人 Goroutine 怎么办?同时处理您的工作(并且很可能是并行的)?

您需要在代码中添加/更改的唯一内容是:要真正启动其中多个:

for i := 0; i < 10; i++ {
    wg.Add(1)
    go ProcessToDo(todo)
}

无需更改任何其他内容,您现在就有了一个正确的并发应用程序,它使用 10 个并发 goroutine 接收并处理您的作业。而且我们没有使用任何“丑陋”的东西time.Sleep()(我们使用了一个,但只是为了模拟缓慢的处理,而不是等待其他 goroutine),并且您不需要任何额外的同步。

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

缓冲区为空后关闭“worker”go 例程 的相关文章

  • Go 中的切片分块

    我有一个切片 其中包含约 210 万条日志字符串 我想创建一个切片切片 其中字符串尽可能均匀分布 这是我到目前为止所拥有的 logs is a slice with 2 1 million strings in it var divided
  • 如何使用 mongo-go-driver 有效地将 bson 转换为 json?

    我想将 bson 转换为mongo go 驱动程序 https github com mongodb mongo go driver有效地转换为 json 我应该小心处理NaN 因为json Marshal失败如果NaN存在于数据中 例如
  • 非法监控状态异常

    如何将轮询线程传递给另一个线程进行处理 程序执行在控制器类中 该类具有 main 方法和线程池 主类控制器 public static void main String args throws InterruptedException Ru
  • Ajax 将文件上传到内容类型为 Multipart 的 GoLang 服务器

    我正在尝试使用多部分表单将音频文件上传到 Golang 服务器 然而 Go 返回错误 multipart NextPart bufio buffer full 我相信这表明我的 Javascript 请求中存在不属于多部分格式的内容 这是我
  • 如何顺序运行 golang 测试?

    当我跑步时go test 我的输出 FAIL TestGETSearchSuccess 0 00s Location drivers api test go 283 Error Not equal 200 expected 204 actu
  • 没有公平性的DelayQueue有问题吗?

    在 Java 7 中 DelayQueue 的实现使用没有公平策略的 ReentrantLock 从长远来看 这是一个问题吗 线程会因此而饿死吗 Thanks 如果您考虑ScheduledThreadPoolExecutor 或任何其他生产
  • ReverseProxy取决于golang中的request.Body

    我想构建一个 http 反向代理 它检查 HTTP 主体 然后将 HTTP 请求发送到它的上游服务器 你怎么能在 Go 中做到这一点 初始尝试 如下 失败 因为 ReverseProxy 复制传入请求 修改它并发送 但正文已被读取 func
  • 如何使用 Java 原生接口从 Java 调用 Go 函数?

    可以通过以下方式调用 C 方法JNA https en wikipedia org wiki Java Native AccessJava 中的接口 如何使用 Go 实现相同的功能 package main import fmt impor
  • 在 Go 中使用互斥锁

    我想了解互斥体是如何工作的 据我目前的理解 它是为了进行原子操作并同步对某些数据的访问 我在这里构建了一个队列数据结构的示例 https github com arnauddri algorithms blob master data st
  • 如何在 Visual Studio Code 中使用 Delve 调试器进行远程调试

    我已经问过了 得到了很好的答复answer https stackoverflow com questions 39058823 how to use delve debugger in visual studio code用于使用 del
  • 从 []byte 到 char*

    我想包装一个 C 函数 它需要一个char 指向非空字节缓冲区 的第一个元素 我正在尝试使用 CGo 将其包装在 Go 函数中 以便我可以将其传递给 byte 但我不知道如何进行转换 C 函数签名的简化版本是 void foo char c
  • 当涉及多个渠道时,select 如何工作?

    我发现在多个非缓冲通道上使用 select 时 例如 select case lt chana case lt chanb 即使两个通道都有数据 但在处理此选择时 case chana 和 case chanb 的跟注不平衡 package
  • Java Executor 根据 CPU 和 RAM 使用情况调整线程池

    我的应用程序使用 Executor 为大量任务提供线程池 通过分析和基准测试 我确定当每个核心有多个线程时 我的应用程序运行速度最快 一个好的启发式方法是从每个核心 4 个线程开始 不断变化 直到达到 gt 90 CPU 或 gt 90 R
  • 有没有办法在 VSCode 中保存时运行 go 测试,并将其输出到终端?

    现在我有几个项目在VSCode中运行 运行起来相当繁琐go test每次我编写新代码时 我宁愿立即看看我是否破坏了某些东西 我知道在 Javascript 中我可以在每次保存文件时运行测试 并将输出发送到终端 现在我正在使用 保存时运行 h
  • Golang 正则表达式命名组和子匹配

    我正在尝试匹配正则表达式并获取匹配的捕获组名称 当正则表达式仅与字符串匹配一次时 这是有效的 但如果它与字符串匹配多次 SubexpNames不返回重复的名称 这是一个例子 package main import fmt regexp fu
  • 使用 HTTPS GRC 从 AWS Codecommit 获取私有存储库

    我正在尝试导入位于 AWS codecommit 中的模块 为了克隆存储库 我使用 HTTPS GRC Git 远程代码提交 方法 该方法使用 Google Suite 凭证来访问 AWS 控制台 我用来克隆存储库的命令是 git clon
  • 模板中的 bson.ObjectId

    我有一个具有 bson ObjectId 类型的结构 例如如下所示 type Test struct Id bson ObjectId Name string Foo string 我想在 html 模板中呈现它 Name Food a h
  • 如何在 Go 中将环境变量传递给测试用例

    在为 Go 编写测试用例时 传递需要提供给测试的环境变量的标准方法是什么 例如 我们不想在测试用例的源代码中嵌入密码 处理这个问题最标准的方法是什么 我们让测试用例寻找配置文件吗 还有别的事吗 看来我偶然发现了答案 将其添加到测试用例中可以
  • 解组转义 XML

    在 Go 中 我将如何解码此 XML 响应 我尝试过建立一个自定义UnMarshal方法在我的Answerstruct 但我运气不太好
  • 在 Go 中初始化嵌入结构

    我有以下内容struct其中包含一个net http Request type MyRequest struct http Request PathParams map string string 现在我想初始化匿名内部结构http Req

随机推荐

  • ER_NOT_SUPPORTED_AUTH_MODE - MySQL 服务器

    连接失败Node js Server to MySQL Database 我有MariaDB 安装在 Node js 服务器 但我决定改用 SQL 数据库 我卸载了 彻底删除了 玛丽亚数据库 之后 我开始安装 社区埃德 MySQL 数据库
  • 删除具有破坏性——但并非总是如此?

    我对 Common Lisp 的破坏性 DELETE 函数有点困惑 它似乎按预期工作 除非该项目是列表中的第一项 CL USER gt defvar test list 1 2 3 TEST CL USER gt delete 1 test
  • cuda:设备函数内联和不同的.cu 文件

    两个事实 CUDA 5 0 允许您在不同的对象文件中编译 CUDA 代码 以便稍后链接 CUDA 架构 2 x 不再自动内联函数 像往常一样 在 C C 中 我实现了一个函数 device int foo in functions cu并将
  • try... except... except... :如何避免重复代码

    我想避免写作errorCount 1在不止一处 我正在寻找比更好的方法 success False try else success True finally if success storage store commit else sto
  • MATLAB 中神经网络分类的 10 倍交叉验证示例

    我正在寻找在神经网络中应用 10 倍交叉验证的示例 我需要这个问题的链接答案 MATLAB 中 10 重 SVM 分类的示例 我想对所有 3 个类别进行分类 而在示例中只考虑了两个类别 编辑 这是我为 iris 示例编写的代码 load f
  • 将结果集中的值写入文本文件(.txt 文件)

    请帮助我处理下面的代码 因为我想将结果集中的值写入 txt 文件 Code while rs next FileWriter fstream new FileWriter file BufferedWriter out new Buffer
  • 删除列标题中“.”之后的字符

    我有一个与这里提出的问题类似的问题 r 删除列名中某些字符之后的部分不过我有一点皱纹 我的列标题的格式如 ENSG00000124564 16 和 ENSG00000257509 1 但是我想删除后面的所有字符 我不能只删除最后 x 个字符
  • ie9 不尊重 img 元素比例中的 SVG

    CSS img max height 30px HTML img src foo svg 我正在寻找这个 svg 图像按比例缩放到最大高度 30 像素高 svg 的自然尺寸为 200 像素 x 200 像素 在 FF 和 Chrome 30
  • 如何获取.NET 3.5中CSIDL_COMMON_DOCUMENTS的路径?

    我正在为安装程序执行自定义操作 它必须读取存储在的文件CSIDL COMMON DOCUMENTS以确定安装目录 我希望在自定义操作中更改安装目录不会成为问题 但这是另一个问题 我看到 NET 4 添加了CommonDocuments to
  • 如何更改ggplot中图例“键”的方向?

    如何更改下面标题的键 使其处于水平位置而不更改图形的垂直线 set seed 000 m lt matrix rnorm 100 0 1 100 1 dt lt data frame m names dt lt c X library gg
  • 根据两个变量匹配数据帧行(索引)

    我本质上是试图将杂乱的数据转换成长形式以进行线性建模 我有 2 个 data frames rec 和 book book 中的每一行都需要根据行中匹配的两个变量 MRN 和 COURSE 粘贴到 rec 的几行的末尾 我已尝试以下方法及其
  • 在 Python 中从 json 数组中选取数据对象

    我有这个数据对象 我想知道如何选择名为的子对象commits or projects 我试过all commits all data commits 但 python 强迫我给它一个整数而不是字符串 想法 commits project i
  • cudaatomicAdd 示例无法产生正确的输出

    编写以下代码的目的是将 100 个元素的浮点数数组增加 1 十倍 在输出中 我期望每个元素都有 10 0f 值的 100 个元素数组 相反 我得到随机值 您能在这里指出我的错误吗 global void testAdd float a fl
  • AutoCompleteTextView 搜索部分单词而不是整个单词

    我有一个AutoCompleteTextView 并且它工作得很好 直到我在输入中添加一个空格 比如说 如果我有许多历史事件的清单 不列颠之战 1940 突出部之战 1944 插入大量战斗 拿破仑的致命进军 1812 插入许多其他历史事件
  • 数据库中电子邮件地址的最佳长度是多少?

    这是我的查询的摘录部分 反映了EMAIL ADDRESS列数据类型和属性 EMAIL ADDRESS CHARACTER VARYING 20 NOT NULL 然而 约翰 桑德斯 uses VARYING 256 这表明我不一定正确理解
  • Java:加载依赖于其他库的库

    我想在我的 java 应用程序中加载我自己的本机库 这些本机库依赖于第三方库 当我的应用程序安装在客户端计算机上时 第三方库可能存在 也可能不存在 在我的 java 应用程序中 我要求用户指定依赖库的位置 获得此信息后 我将使用它通过 JN
  • 在我网站的图像上添加 Instagram 滤镜

    我在用CSSGram在我的网站上使图像具有类似 Instagram 的滤镜 下面是向图像添加滤镜的方法
  • android中声音转换为频率[重复]

    这个问题在这里已经有答案了 可能的重复 实时音高检测 确定通过麦克风接收到的声音的音频频率 我正在开发自己的 Android 吉他调音器 在这里我将不得不使用吉他音符频率 因此 我想知道如何在android中将通过麦克风端口传来的声音转换为
  • 在静态类中使用依赖注入

    我需要在静态类中使用依赖注入 静态类中的方法需要注入依赖项的值 以下代码示例演示了我的问题 public static class XHelper public static TResponse Execute string metodo
  • 缓冲区为空后关闭“worker”go 例程

    我想要我的 goroutine 工作人员 ProcessToDo 在下面的代码中 等待所有 排队 工作处理完毕后再关闭 工作例程有一个 待办事项 通道 缓冲 通过该通道将工作发送给它 它有一个 完成 通道来告诉它开始关闭 文档称 如果满足多