如何从数据竞赛中修复此 golang 代码?

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

不明白为什么函数 testgo() 只返回一半结果?

package main

import (
    "sync"
)

var wg sync.WaitGroup

type myStruct struct {
    sync.Mutex
    text string
}

func main() {
    res := testgo()
    print(res)
}

func testgo() string {

    var ms myStruct
    ch := make(chan string)

    for j := 0; j < 10; j++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            ch <- "1\n"
        }()
    }

    go func() {
        wg.Wait()
        close(ch)
    }()

    for range ch {
        ms.Lock()
        ms.text += <-ch
        ms.Unlock()
    }

    return ms.text
}

预计输出字符串中有 10 位数字,但实际只得到 5 位。 (https://go.dev/play/p/KzZdTCoHJXa

为什么?如何解决这个问题?

输出

1
1
1
1
1
go concurrency channel
1个回答
-1
投票

要实现一个在程序终止之前接收并打印所有十条消息的工作解决方案,有一些可行的方法。

以下方法希望您知道您的接收者应该收到多少条消息(十条):

var wgSender sync.WaitGroup
var wgReceiver sync.WaitGroup

type myStruct struct {
    sync.Mutex
    text string
}

func testgo() string {

    var ms myStruct
    ch := make(chan string)

    for j := 0; j < 10; j++ {
        wgSender.Add(1)
        go func() {
            defer wgSender.Done()
            ch <- "1\n"
        }()
    }
    for j := 0; j < 10; j++ {
        wgReceiver.Add(1)
        go func() {
            defer wgReceiver.Done()
            ms.Lock()
            ms.text += <-ch
            ms.Unlock()
        }()
    }

    wgSender.Wait()
    wgReceiver.Wait()
    close(ch)

    return ms.text
}

您还可以像这样使用具有更大缓冲区大小的通道,这样您就可以发送高达特定上限的消息。通过这种方式,您可以删除互斥锁,并且只需要为您的发件人提供一个 WaitGroup。接收端将同步传输您(关闭的)频道的内容:

var wgSender sync.WaitGroup

type myStruct struct {
    text string
}

func testgo() string {

    var ms myStruct
    ch := make(chan string, 99)

    for j := 0; j < 10; j++ {
        wgSender.Add(1)
        go func() {
            defer wgSender.Done()
            ch <- "1\n"
        }()
    }

    wgSender.Wait()
    close(ch)
    
    for message := range ch {
        ms.text += message
    }

    return ms.text
}

第三个选项是使用通道大小 1 并使用 select 语句接收,只要您的通道接收消息直到发生超时:

var wgSender sync.WaitGroup

type myStruct struct {
    text string
}

func testgo() string {

    var ms myStruct
    ch := make(chan string)

    for j := 0; j < 10; j++ {
        wgSender.Add(1)
        go func() {
            defer wgSender.Done()
            ch <- "1\n"
        }()
    }

    timeoutReached := false
    for !timeoutReached {
        select {
        case <-time.After(1 * time.Second):
            // timeout occured here, no more sender routines send anything for 1 second
            timeoutReached = true
        case message := <-ch:
            ms.text += message
        }
    }

    wgSender.Wait()
    close(ch) // optionally close the channel

    return ms.text
}

所有这三种实现都保证在程序终止之前接收并打印所有 10 条消息。希望对你有帮助

© www.soinside.com 2019 - 2024. All rights reserved.