Go 通道有时收不到最后一个值

问题描述 投票:0回答:1

我目前正在学习Go Channels,我正在尝试这段代码。它创建 10 个 goroutine,每个 goroutine 向通道发送 1000 个 1。然后另一个 go 例程接收它并将其添加到计数器中。我还使用 WaitGroups 来确保 goroutine 在打印结果之前完成。

代码:

package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
)

func main() {
    var counter int

    fmt.Println("\nWithout Channels -------")
    for i := 0; i < 10; i++ {
        go func() {
            for j := 0; j < 1000; j++ {
                // to simulate race condition
                time.Sleep(time.Duration(1))
                counter++
            }
        }()
    }

    runtime.Gosched()

    fmt.Println("Expected counter: 10000, Actual counter:", counter)

    fmt.Println("\nWith Channels -------")
    for i := 0; i < 100; i++ {
        WithChannels()
    }
}

func WithChannels() {
    var counter int
    ch := make(chan int)
    var wg sync.WaitGroup
    // var wg2 sync.WaitGroup

    wg.Add(10)
    for i := 0; i < 10; i++ {
        go func() {
            defer wg.Done()
            for j := 0; j < 1000; j++ {
                time.Sleep(time.Duration(1))
                ch <- 1
            }
        }()
    }

    // wg2.Add(1)
    go func() {
        for {
            increment, ok := <-ch
            if !ok {
                // channel closed, break
                break
            }
            counter += increment
        }
        // wg2.Done()
    }()

    wg.Wait()
    // wg2.Wait()
    // time.Sleep(time.Duration(1) * time.Second)
    fmt.Println("Expected counter: 10000, Actual counter:", counter)
    close(ch)
}

输出:

Without Channels -------
Expected counter: 10000, Actual counter: 139

With Channels -------
Expected counter: 10000, Actual counter: 9999
Expected counter: 10000, Actual counter: 10000
Expected counter: 10000, Actual counter: 9999
Expected counter: 10000, Actual counter: 10000
Expected counter: 10000, Actual counter: 10000
Expected counter: 10000, Actual counter: 9999
...

我预计每次运行计数器时计数器都会为 10,000,但有时它似乎没有收到最后的值。我尝试为接收 goroutine 添加另一个 WaitGroup (在评论中看到),但这会导致死锁。

我在这里做错了什么吗?

go concurrency deadlock channel race-condition
1个回答
0
投票

您正在 Goroutine 中从通道接收数据,但从未等待它完成工作。您可以使用 Goroutine 来等待发送者完成并关闭通道,然后在主 Goroutine 中从通道接收数据。

func WithChannels() {
    var counter int
    ch := make(chan int)
    var wg sync.WaitGroup

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for j := 0; j < 1000; j++ {
                time.Sleep(time.Duration(1))
                ch <- 1
            }
        }()
    }

    // Use a gorutine to wait for all the senders to complete and then close the channel.
    go func() {
        wg.Wait()
        close(ch)
    }()

    // Receive from the channel until it's closed.
    for {
        increment, ok := <-ch
        if !ok {
            // channel closed, break
            break
        }
        counter += increment
    }
    
    fmt.Println("Expected counter: 10000, Actual counter:", counter)
}
© www.soinside.com 2019 - 2024. All rights reserved.