首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Golang并发性:如何从不同的goroutine追加到同一片

Golang并发性:如何从不同的goroutine追加到同一片
EN

Stack Overflow用户
提问于 2013-08-29 06:30:24
回答 3查看 47K关注 0票数 40

我有一个并发的goroutines,它想要将一个结构(指向一个指针)附加到同一个片上。如何在Go中编写它以使其并发性安全?

这将是我的并发不安全代码,使用一个等待组:

代码语言:javascript
运行
复制
var wg sync.WaitGroup
MySlice = make([]*MyStruct)
for _, param := range params {
    wg.Add(1)
    go func(param string) {
        defer wg.Done()
        OneOfMyStructs := getMyStruct(param)
        MySlice = append(MySlice, &OneOfMyStructs)
    }(param)
}
wg.Wait()

我猜你需要使用go通道来保证并发安全。有没有人能举个例子?

EN

回答 3

Stack Overflow用户

发布于 2013-08-29 07:02:39

用sync.Mutex保护MySlice = append(MySlice, &OneOfMyStructs)并没有什么错。当然,您可以有一个缓冲区大小为len(params)的结果通道,所有goroutines都会发送它们的答案,一旦您的工作完成,您就可以从这个结果通道中收集数据。

如果您的params具有固定大小:

代码语言:javascript
运行
复制
MySlice = make([]*MyStruct, len(params))
for i, param := range params {
    wg.Add(1)
    go func(i int, param string) {
         defer wg.Done()
         OneOfMyStructs := getMyStruct(param)
         MySlice[i] = &OneOfMyStructs
     }(i, param)
}

因为所有的goroutines都写到不同的内存中,所以这并不是很好用。

票数 42
EN

Stack Overflow用户

发布于 2016-08-21 22:38:12

@jimt发布的答案并不完全正确,因为它遗漏了通道中发送的最后一个值,并且从未调用过最后一个defer wg.Done()。下面的代码片段进行了更正。

https://play.golang.org/p/7N4sxD-Bai

代码语言:javascript
运行
复制
package main

import "fmt"
import "sync"

type T int

func main() {
    var slice []T
    var wg sync.WaitGroup

    queue := make(chan T, 1)

    // Create our data and send it into the queue.
    wg.Add(100)
    for i := 0; i < 100; i++ {
        go func(i int) {
            // defer wg.Done()  <- will result in the last int to be missed in the receiving channel
            queue <- T(i)
        }(i)
    }

    go func() {
        // defer wg.Done() <- Never gets called since the 100 `Done()` calls are made above, resulting in the `Wait()` to continue on before this is executed
        for t := range queue {
            slice = append(slice, t)
            wg.Done()   // ** move the `Done()` call here
        }
    }()

    wg.Wait()

    // now prints off all 100 int values
    fmt.Println(slice)
}
票数 25
EN

Stack Overflow用户

发布于 2013-08-29 07:02:07

频道是解决这个问题的最好方法。下面是一个可以在go playground上运行的示例。

代码语言:javascript
运行
复制
package main

import "fmt"
import "sync"
import "runtime"

type T int

func main() {
    var slice []T
    var wg sync.WaitGroup

    queue := make(chan T, 1)

    // Create our data and send it into the queue.
    wg.Add(100)
    for i := 0; i < 100; i++ {
        go func(i int) {
            defer wg.Done()

            // Do stuff.
            runtime.Gosched()

            queue <- T(i)
        }(i)
    }

    // Poll the queue for data and append it to the slice.
    // Since this happens synchronously and in the same
    // goroutine/thread, this can be considered safe.
    go func() {
        defer wg.Done()
        for t := range queue {
            slice = append(slice, t)
        }
    }()

    // Wait for everything to finish.
    wg.Wait()

    fmt.Println(slice)
}

注释:之所以会有runtime.Gosched()调用,是因为这些goroutines不会向调度器让步。如果我们不显式地做一些事情来触发所述调度程序,这将导致死锁。另一个选项可能是执行一些I/O (例如:打印到stdout)。但我发现runtime.Gosched()的意图更简单、更清晰。

票数 -2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18499352

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档