


What is the best practice in Golang for tracking the completion status of two Goroutines in a third Goroutine? In Golang, to track the completion status of two Goroutines and process their results in a third Goroutine, the best practice is to use WaitGroup from the sync package. WaitGroup allows us to wait in the main Goroutine for the completion of other Goroutines. First, we need to create a WaitGroup object and call the Add method in the main Goroutine to set the number of waiting Goroutines. Then, the Done method is called at the end of each Goroutine to signal the completion of that Goroutine. Finally, the Wait method is called in the third Goroutine to wait for all Goroutines to complete. This way, we can safely track and process the results of both Goroutines. This is the best practice in Golang for tracking the completion status of multiple Goroutines.
Question content
I have three goroutines running concurrently. Two of them do some processing and send their results to the result channel. The third goroutine "counts" the results by reading the result channels. I could use a waitgroup to wait for the two computation goroutines to complete and then iterate over the result channels to tally the results, but this doesn't scale and requires me to create a buffered result channel with a huge buffer size, which is unacceptable in production code.
I want to count the results as processing occurs, but I don't want to exit the program before all statistics are completed. What are the best practices for achieving this in Go?
This is my current method and it works great. I'm wondering if there's a better way as this seems a bit clunky?
package main import ( "fmt" "sync" ) type T struct{} func main() { var widgetInventory int = 1000 transactions := make(chan int, 100) salesDone := make(chan T) purchasesDone := make(chan T) var wg sync.WaitGroup fmt.Println("Starting inventory count = ", widgetInventory) go makeSales(transactions, salesDone) go newPurchases(transactions, purchasesDone) wg.Add(1) go func() { salesAreDone := false purchasesAreDone := false for { select { case transaction := <-transactions: widgetInventory += transaction case <-salesDone: salesAreDone = true case <-purchasesDone: purchasesAreDone = true default: if salesAreDone && purchasesAreDone { wg.Done() return } } } }() wg.Wait() fmt.Println("Ending inventory count = ", widgetInventory) } func makeSales(transactions chan int, salesDone chan T) { for i := 0; i < 3000; i++ { transactions <- -100 } salesDone <- struct{}{} } func newPurchases(transactions chan int, purchasesDone chan T) { for i := 0; i < 3000; i++ { transactions <- 100 } purchasesDone <- struct{}{} }
Solution
Doesn't fit any reasonable definitionGood. You have a hot for
loop here:
for { select { case transaction := <-transactions: widgetInventory += transaction case <-salesDone: salesAreDone = true case <-purchasesDone: purchasesAreDone = true default: if salesAreDone && purchasesAreDone { wg.Done() return } } }
As long as there is no channel to read from, the default
case will be executed. This happens a lot because of the way channels work.
This slightly adjusted version of the code illustrates the "heat" of this loop. Exact results will vary and may be quite high.
Default case ran 27305 times
You don't want a default
situation when select
ing from a channel, unless that default also blocks something in it. Otherwise you'll get thermal cycling like this.
Better approach: Use nil
able channels for selection
Typically in a select you want to identify a closed channel and set the channel variable to nil
; select
will never succeed from nil
channel reads content, so this effectively "disables" the selection.
Consider this modified version of the code :
go func(transactions chan int, salesDone <-chan T, purchasesDone <-chan T) { defer wg.Done() for transactions != nil { select { case transaction, ok := <-transactions: if ok { widgetInventory += transaction } else { transactions = nil } case <-salesDone: salesDone = nil if purchasesDone == nil { close(transactions) } case <-purchasesDone: purchasesDone = nil if salesDone == nil { close(transactions) } } } }(transactions, salesDone, purchasesDone)
With these adjustments to the consumer, we no longer have hot loops; we always block until data is read from the channel. Once both salesDone
and purchasesDone
are "signaled", we close(transactions)
. Once we exhaust transactions
and it is closed, we set transactions
to nil. We loop when transactions
is not nil, which in this code means all channels are nil
.
Subtle but important point: I am passing a channel to this function so its reference does not share scope with main
. Otherwise, setting transactions
to nil
will write to a variable shared between goroutines. In this case however, it doesn't matter anyway because we "know" we are the last to read from transactions
.
Simpler option: multiple wait groups
If you think about what you're doing here, you need to wait until both producers have finished producing transactions
. Then you want to drain transactions
. Once the channel is closed and drained, main
knows the summation is complete.
You don't need select
to perform this operation. And select
having a case for each "worker" is arguably rather inelegant; you have to hardcode multiple workers and handle the "completion" channel individually.
What you need to do is:
- 除了为生产者使用一个
var resultswgsync.WaitGroup
之外,还为消费者添加一个。 - 生产者
defer wg.Done()
- 消费者
defer resultswg.Done()
在遍历transactions
之前:go func() { defer resultswg.Done() for transaction := range transactions { widgetInventory += transaction } }()
- main 处理等待生产者、关闭事务以结束范围,然后等待消费者:
wg.Wait() close(transactions) resultswg.Wait()
package main import ( "fmt" "sync" ) func main() { var widgetInventory int = 1000 transactions := make(chan int, 100) var wg, resultswg sync.WaitGroup fmt.Println("Starting inventory count = ", widgetInventory) wg.Add(2) go makeSales(transactions, &wg) go newPurchases(transactions, &wg) resultswg.Add(1) go func() { defer resultswg.Done() for transaction := range transactions { widgetInventory += transaction } }() wg.Wait() close(transactions) resultswg.Wait() fmt.Println("Ending inventory count = ", widgetInventory) } func makeSales(transactions chan int, wg *sync.WaitGroup) { defer wg.Done() for i := 0; i < 3000; i++ { transactions <- -100 } } func newPurchases(transactions chan int, wg *sync.WaitGroup) { defer wg.Done() for i := 0; i < 3000; i++ { transactions <- 100 } }
您可以在这里看到,在此模式中可以有任意数量的生产者;您只需为每个生产者添加 wg.Add(1)
即可。
当我不知道每个工作人员会返回多少结果时,我一直使用这种模式来并行化工作。我发现它很容易理解,并且比尝试 select
多个通道简单得多。事实上,我什至想说,如果您发现自己从多个渠道进行 select
ing,您应该退后一步,确保它对您来说确实有意义。我使用 select
的频率远远低于使用等待组的频率。
The above is the detailed content of What is the best practice in Golang for tracking the completion status of two Goroutines within a third Goroutine?. For more information, please follow other related articles on the PHP Chinese website!

Q:我有一个问题,我想要监视Linux系统的内存使用情况。在Linux下有哪些可用的视图或命令行工具可以使用呢?A:在Linux系统中,有多种方法可以监视内存使用情况。下面是一些通过视图工具或命令行来查看内存使用情况的方法。/proc/meminfo:最简单的方法是查看/proc/meminfo文件。这个虚拟文件会动态更新,并提供了关于内存使用情况的详细信息。它列出了各种内存指标,可以满足你对内存使用情况的大部分需求。另外,你还可以通过/proc//statm和/proc//status来查看进

“我们将介绍几款适用于Linux系统的白板应用程序,相信这些信息对您会非常有帮助。请继续阅读!”一般来说,数字白板是一种用于大型互动显示面板的工具,常见的设备类型包括平板电脑、大屏手机、触控笔记本和表面显示设备等。当教师使用白板时,您可以使用触控笔、手写笔、手指甚至鼠标在设备屏幕上进行绘画、书写或操作元素。这意味着您可以在白板上拖动、点击、删除和绘画,就像在纸上使用笔一样。然而,要实现这一切,需要有一款软件来支持这些功能,并实现触控和显示之间的精细协调。目前市面上有许多商业应用可以完成这项工作。

一、TensorRT-LLM的产品定位TensorRT-LLM是NVIDIA为大型语言模型(LLM)开发的可扩展推理方案。它基于TensorRT深度学习编译框架构建、编译和执行计算图,并借鉴了FastTransformer中高效的Kernels实现。此外,它还利用NCCL实现设备间的通信。开发者可以根据技术发展和需求差异,定制算子以满足特定需求,例如基于cutlass开发定制的GEMM。TensorRT-LLM是NVIDIA官方推理方案,致力于提供高性能并不断完善其实用性。TensorRT-LL

ZRX(0x)是一个基于以太坊区块链的开放协议,用于实现分布式交易和去中心化交易所(DEX)功能。作为0x协议的原生代币,ZRX可用于支付交易费用、治理协议变更和获取平台优惠。1.ZRX币升值空间展望:从技术角度来看,ZRX作为0x协议的核心代币,在去中心化交易所的应用逐渐增多,市场对其认可度也在增加。以下是几个关键因素,有助于提升ZRX币的价值空间:市场需求潜力大、社区活跃度高、开发者生态繁荣等。这些因素共同促进了ZRX的广泛应用和使用,进而推动了其市场价格的上升。市场需求的增长潜力,意味着更

busybox概述众所周知,在Linux环境下,一切皆文件,文件可以表示一切。而文件系统则是这些普通组件的集合。在嵌入式领域中,常常使用基于busybox构建的rootfs来构建文件系统。busybox诞生至今已有近20年的历史,如今已成为嵌入式行业中主流的rootfs构建工具。busybox的代码是完全开源的。你可以进入官方网站,点击”GetBusyBox”下面的”DownloadSource”进入源码下载界面。“官方网站链接:https://busybox.net/”2.busybox的配置

BOSS直聘怎么创建多个简历?BOSS直聘是很多小伙伴找工作的一大招聘平台,为用户们提供了非常多便利的求职服务。各位在使用BOSS直聘的时候,可以创建多个不同的简历,以便投送到不同的工作岗位上,获取到更高成功率的求职操作,各位如果对此感兴趣的话,就随小编一起来看看BOSS直聘双简历创建教程吧。BOSS直聘怎么创建多个简历1.登录Boss直聘:在您的电脑或手机上,登录您的Boss直聘账户。2.进入简历管理:在Boss直聘首页,点击“简历管理”,进入简历管理页面。3.创建新简历:在简历管理页面,点击

最近,我正在进行一个项目,遇到了一个问题。在ARM上运行的ThreadX与DSP通信时采用了消息队列的方式传递消息(最终实现使用了中断和共享内存的方法)。然而,在实际的操作过程中,发现ThreadX经常崩溃。经过排查,发现问题出在传递消息的结构体没有考虑字节对齐的问题上。我想顺便整理一下关于C语言中字节对齐的问题,并与大家分享。一、概念字节对齐与数据在内存中的位置有关。如果一个变量的内存地址恰好是它长度的整数倍,那么它就被称为自然对齐。例如,在32位CPU下,假设一个整型变量的地址为0x0000

幻兽帕鲁游戏开局就可以拿三个宝箱和两个宠物蛋,拿完以后就可以开始建家了,建家的地址一定要选好,要不然开局会发现自己的家没有了,然后就是捕捉一直适合自己的帕鲁,作为自己的伙伴或者打工人即可。


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

Dreamweaver Mac version
Visual web development tools

mPDF
mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

PhpStorm Mac version
The latest (2018.2.1) professional PHP integrated development tool

SublimeText3 Chinese version
Chinese version, very easy to use

MinGW - Minimalist GNU for Windows
This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.
