目录
问题内容
解决方法
更好的方法:使用 nilable 通道进行选择
首页 后端开发 Golang Golang 中跟踪第三个 Goroutine 中两个 Goroutine 的完成状态的最佳实践是什么?

Golang 中跟踪第三个 Goroutine 中两个 Goroutine 的完成状态的最佳实践是什么?

Feb 11, 2024 pm 02:54 PM
overflow

Golang 中跟踪第三个 Goroutine 中两个 Goroutine 的完成状态的最佳实践是什么?

Golang 中跟踪第三个 Goroutine 中两个 Goroutine 的完成状态的最佳实践是什么? 在 Golang 中,要跟踪两个 Goroutine 的完成状态并在第三个 Goroutine 中处理它们的结果,最佳实践是使用 sync 包中的 WaitGroup。WaitGroup 允许我们在主 Goroutine 中等待其他 Goroutine 的完成。首先,我们需要创建一个 WaitGroup 对象,并在主 Goroutine 中调用 Add 方法来设置等待的 Goroutine 数量。然后,在每个 Goroutine 的末尾调用 Done 方法来表示该 Goroutine 的完成。最后,在第三个 Goroutine 中调用 Wait 方法来等待所有 Goroutine 的完成。这样,我们就可以安全地跟踪并处理两个 Goroutine 的结果了。这是 Golang 中跟踪多个 Goroutine 完成状态的最佳实践。

问题内容

我有三个并发运行的 goroutine。其中两个进行一些处理并将其结果发送到结果通道。第三个 goroutine 通过读取结果通道来“统计”结果。我可以使用 waitgroup 等待两个计算 goroutine 完成,然后遍历结果通道来统计结果,但这无法扩展,并且需要我创建一个具有巨大缓冲区大小的缓冲结果通道,这是不可接受的在生产代码中。

我想在处理发生时统计结果,但在所有统计完成之前我不想退出程序。在 Go 中实现这一目标的最佳实践是什么?

这是我目前的方法,效果很好。我想知道是否有更好的方法,因为这看起来有点笨拙?

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{}{}
}
登录后复制

解决方法

不适合任何合理的定义很好。您在这里有一个热门的 for 循环:

for {
            select {
            case transaction := <-transactions:
                widgetInventory += transaction
            case <-salesDone:
                salesAreDone = true
            case <-purchasesDone:
                purchasesAreDone = true
            default:
                if salesAreDone && purchasesAreDone {
                    wg.Done()
                    return
                }
            }
        }
登录后复制

只要没有通道可供读取,就会执行 default 案例。由于渠道的工作方式,这种情况经常发生。

这个稍作调整的代码版本说明了此循环的“热度”。确切的结果会有所不同,可能会相当高。

Default case ran 27305 times
登录后复制

selecting 来自通道时,您不希望出现 selecting 来自通道时,您不希望出现 default 情况,除非该默认情况也会阻止其中的某些内容。否则你会得到这样的热循环。

更好的方法:使用 nilable 通道进行选择

通常在选择中,您想要识别关闭的通道并将通道变量设置为 nilselect 永远不会成功地从 nil

select 永远不会成功地从

通道读取内容,因此这实际上“禁用”了该选择。 考虑

代码的此修改版本

salesDonepurchasesDone 都被“发出信号”,我们 close(transactions)。一旦我们耗尽 transactions 并且它被关闭,我们将 transactions 设置为 nil。我们在 transactions 不为 nil 时循环,在这段代码中,意味着所有通道都是 nil

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)
登录后复制

通过对消费者的这些调整,我们不再有热循环;我们总是阻塞直到从通道读取数据。一旦 salesDonepurchasesDone 都被“发出信号”,我们 close(transactions)。一旦我们耗尽 transactions

并且main 共享范围。否则,将 transactions 设置为 nil 将写入一个在 goroutine 之间共享的变量。然而在这种情况下,无论如何,这并不重要,因为我们“知道”我们是最后一个从 transactions它被关闭,我们将 transactions 设置为 nil。我们在 transactions 不为 nil 时循环,在这段代码中,意味着所有通道都是

微妙但重要的一点:我将通道传递给此函数,因此它的引用不与 main 共享范围。否则,将 transactions 设置为

将写入一个在 goroutine 之间共享的变量。然而在这种情况下,无论如何,这并不重要,因为我们“知道”我们是最后一个从 transactions 读取的内容。transactions 的生产。然后你想排空 transactions。一旦通道关闭并排空,main

更简单的选项:多个等待组

select 来执行此操作。而 select如果您考虑一下您在这里所做的事情,您需要等到两个生产者都完成对

就知道求和已完成。

您不需要 🎜 为每个“工人”都有一个案例,可以说是相当不优雅的;您必须对多个工作人员进行硬编码并单独处理“完成”通道。🎜 🎜您需要做的是:🎜
  • 除了为生产者使用一个 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 多个通道简单得多。事实上,我什至想说,如果您发现自己从多个渠道进行 selecting,您应该退后一步,确保它对您来说确实有意义。我使用 select 的频率远远低于使用等待组的频率。

以上是Golang 中跟踪第三个 Goroutine 中两个 Goroutine 的完成状态的最佳实践是什么?的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

比特币诞生至今价格2009-2025 最全的BTC历史价格汇总 比特币诞生至今价格2009-2025 最全的BTC历史价格汇总 Jan 15, 2025 pm 08:11 PM

自 2009 年问世以来,比特币成为加密货币界的领头羊,其价格经历了巨大的波动。为了提供全面的历史概述,本文汇集了从 2009 年到 2025 年的比特币价格数据,涵盖了重大的市场事件、市场情绪变化和影响价格走势的重要因素。

比特币诞生至今历史价格总览 比特币历史价格趋势大全 比特币诞生至今历史价格总览 比特币历史价格趋势大全 Jan 15, 2025 pm 08:14 PM

比特币,作为一种加密货币,自问世以来经历了显着的市场波动。本文将提供比特币自诞生以来的历史价格总览,帮助读者了解其价格趋势和关键时刻。通过分析比特币的历史价格数据,我们可以了解市场对其价值评估、影响其波动的因素,并为未来投资决策提供依据。

比特币诞生至今历史价格一览 BTC历史价格行情趋势图(最新汇总) 比特币诞生至今历史价格一览 BTC历史价格行情趋势图(最新汇总) Feb 11, 2025 pm 11:36 PM

比特币自 2009 年创世以来,价格经历多次大幅波动,最高涨至 2021 年 11 月的 69,044.77 美元,最低跌至 2018 年 12 月的 3,191.22 美元。截至 2024 年 12 月,最新价格突破 100,204 美元。

2018-2024年比特币最新价格美元大全 2018-2024年比特币最新价格美元大全 Feb 15, 2025 pm 07:12 PM

实时比特币美元价格 影响比特币价格的因素 预测比特币未来价格的指标 以下是 2018-2024 年比特币价格的一些关键信息:

如何通过CSS自定义resize符号并使其与背景色统一? 如何通过CSS自定义resize符号并使其与背景色统一? Apr 05, 2025 pm 02:30 PM

CSS自定义resize符号的方法与背景色统一在日常开发中,我们经常会遇到需要自定义用户界面细节的情况,比如调...

H5页面制作是前端开发吗 H5页面制作是前端开发吗 Apr 05, 2025 pm 11:42 PM

是的,H5页面制作是前端开发的重要实现方式,涉及HTML、CSS和JavaScript等核心技术。开发者通过巧妙结合这些技术,例如使用&lt;canvas&gt;标签绘制图形或使用JavaScript控制交互行为,构建出动态且功能强大的H5页面。

Flex布局下文字超出省略却撑开容器?如何解决? Flex布局下文字超出省略却撑开容器?如何解决? Apr 05, 2025 pm 11:00 PM

Flex布局下文字超出省略导致容器撑开的问题及解决方法在使用Flex...

为什么inline-block元素会出现错位现象?如何解决这个问题? 为什么inline-block元素会出现错位现象?如何解决这个问题? Apr 04, 2025 pm 10:39 PM

关于inline-block元素错位显示的原因及解决方案在编写网页布局时,我们常常会遇到一些看似奇怪的显示问题。比...

See all articles