我有一个切片,其中包含要完成的工作,还有一个切片,它将包含完成所有工作后的结果.以下是我的一般流程简图:

var results = make([]Result, len(jobs))
wg := sync.WaitGroup{}
for i, job := range jobs {
    wg.Add(1)
    go func(i int, j job) {
        defer wg.Done()
        var r Result = doWork(j)
        results[i] = r
    }(i, job)
}
wg.Wait()
// Use results

它似乎起作用了,但我还没有彻底测试过,也不确定这样做是否安全.通常,让多个goroutine写入anything会让我感觉不太好,但在本例中,每个goroutine都被限制到它自己在片中的索引,这是预先分配的.

我想另一种 Select 是通过渠道收集结果,但由于结果的顺序很重要,这似乎相当简单.以这种方式写入切片元素是否安全?

推荐答案

规则很简单:如果多个goroutine同时访问一个variable,并且至少有一个访问是写入,那么就需要同步.

你的例子没有违反这条规则.您不会写入片value(片标题),而只是读取它(隐式地,当您为其编制索引时).

您不读取切片elements,只修改切片元素.并且每个Goroutine仅修改单个、differentdesignated个切片元素.因 for each 片元素都有自己的地址(自己的内存空间),所以它们就像是不同的变量.这是Spec: Variables:美元的费用.

Structuredarrayslicestruct类型的变量分别具有addressed个元素和字段.Each such element acts like a variable.

必须记住的是,如果不进行同步,您将无法读取results切片的结果.您在示例中使用的等待组是足够的同步.一旦wg.Wait()返回,您就可以读取切片,因为这只能在所有调用wg.Done()的工作Goroutine之后发生,并且没有任何Worker Goroutine在它们调用wg.Done()之后修改元素.

例如,这是判断/处理结果的有效(safe)方式:

wg.Wait()
// Safe to read results after the above synchronization point:
fmt.Println(results)

但是如果你想在wg.Wait()之前访问results的元素,那将是一场数据竞赛:

// This is data race! Goroutines might still run and modify elements of results!
fmt.Println(results)
wg.Wait()

Go相关问答推荐

如何创建两个连接的io.ReadWriteClosers以用于测试目的

如何获得与cksum相同的CRC 32?

在字符串与字符串子切片上使用len进行位转移产生意外输出

Go Net/http路由

GORM没有从 struct 创建完整的表,如何修复?

Golang中的泛型 struct /接口列表

在nixos上找不到XInput2.h头文件的包

为什么标准库中的 IsSorted 会反向迭代切片?

Docker 执行失败并显示cmd/ENTRYPOINT 中的命令未找到

Golang chromedp Dockerfile

如何解决构建Docker Compose时的权限被拒绝问题

使用 httptest 对 http 请求进行单元测试重试

接受通道和切片的通用函数

Go 中的 HTTP 请求验证中间件

如何根据 Go 中第二次出现的分隔符拆分字符串?

同时调用应该只获取一次数据

如何在 Windows 上使用 cgo 为 386 arch 构建 lib?

传递上下文的最佳方式

为什么 template.ParseFiles() 没有检测到这个错误?

手动下载并放置一个 golang mod 文件