为了账号安全,请及时绑定邮箱和手机立即绑定

优雅地关闭通道而不是在关闭的通道上发送

优雅地关闭通道而不是在关闭的通道上发送

Go
浮云间 2023-03-15 15:17:02
我是Golang并发的新手,一直在努力理解下面提到的这段代码。我目睹了一些我无法解释为什么会发生的事情:for i <= 100000 {在 main 函数中 使用小于等于 100000 的 i 时,它有时会为 nResults 和 countWrites 打印不同的值(在最后两个语句中)fmt.Printf("number of result writes %d\n", nResults) fmt.Printf("Number of job writes %d\n", jobWrites)当我使用超过 1000000 时,它给出panic: send on closed channel我如何确保发送给作业的值不在关闭的通道上,并且稍后在结果中收到所有值后我们可以关闭通道而不会出现死锁?package mainimport (    "fmt"    "sync")func worker(wg *sync.WaitGroup, id int, jobs <-chan int, results chan<- int, countWrites *int64) {    defer wg.Done()    for j := range jobs {        *countWrites += 1        go func(j int) {            if j%2 == 0 {                results <- j * 2            } else {                results <- j            }        }(j)    }}func main() {    wg := &sync.WaitGroup{}    jobs := make(chan int)    results := make(chan int)    var i int = 1    var jobWrites int64 = 0    for i <= 10000000 {        go func(j int) {            if j%2 == 0 {                i += 99                j += 99            }            jobWrites += 1            jobs <- j        }(i)        i += 1    }    var nResults int64 = 0    for w := 1; w < 1000; w++ {        wg.Add(1)        go worker(wg, w, jobs, results, &nResults)    }    close(jobs)    wg.Wait()    var sum int32 = 0    var count int64 = 0    for r := range results {        count += 1        sum += int32(r)        if count == nResults {            close(results)        }    }    fmt.Println(sum)    fmt.Printf("number of result writes %d\n", nResults)    fmt.Printf("Number of job writes %d\n", jobWrites)}
查看完整描述

1 回答

?
慕村9548890

TA贡献1884条经验 获得超4个赞

您的代码中有很多问题。

在关闭的频道上发送

使用 Go 通道的一个一般原则是

不要从接收端关闭通道,如果通道有多个并发发送者则不要关闭通道

您的解决方案很简单:不要有多个并发发件人,然后您可以从发件人端关闭频道。

与其为添加到通道的每个作业启动数百万个单独的 goroutine,不如运行一个执行整个循环的 goroutine 以将所有作业添加到通道。并在循环后关闭通道。工作人员将尽可能快地使用通道。

通过修改多个 goroutine 中的共享变量来进行数据竞争

您在不采取特殊步骤的情况下修改两个共享变量:

  1. nResults,您将其传递给countWrites *int64工作人员中的。

  2. ijobs在写入作业通道的循环中:您从多个 goroutines 向它添加 99,这使得它无法预测您实际向通道写入了多少值

要解决 1,有很多选项,包括使用sync.Mutex. 但是,由于您只是添加它,所以最简单的解决方案是使用atomic.AddInt64(countWrites, 1)而不是*countWrites += 1

要解决 2,不要在每次写入通道时使用一个 goroutine,而是在整个循环中使用一个 goroutine(见上文)


查看完整回答
反对 回复 2023-03-15
  • 1 回答
  • 0 关注
  • 48 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信