使用 Channels 和 WaitGroup 时所有 GoRoutines 上的死锁

Deadlock on All GoRoutines When Using Channels and WaitGroup

我是 Go 的新手,目前正在尝试 运行 创建文件的函数和 returns 它的文件名,并同时具有此 运行。

我决定尝试使用 goroutines 和 WaitGroup 来完成它。当我使用这种方法时,我最终得到的列表大小比输入大小小几百个文件。例如。对于 5,000 个文件,我创建了大约 4,700~ 个文件。

我认为这是由于某些竞争条件造成的:

wg := sync.WaitGroup{}

filenames := make([]string, 0)

for i := 0; i < totalFiles; i++ {
    wg.Add(1)
    go func() {
        defer wg.Done()
        filenames = append(filenames, createFile())
    }()
}

wg.Wait()

return filenames, nil

不要通过共享内存来交流;通过通信共享内存。

我尝试使用通道“通过通信共享内存”。每当我这样做时,似乎都会出现僵局,我似乎无法理解为什么。谁能给我指出正确的方向,让我正确地一起使用通道和等待组,以便将所有创建的文件保存到共享数据结构中?

这是为我产生死锁的代码(致命错误:所有 goroutines 都睡着了 - 死锁!):

wg := sync.WaitGroup{}

filenames := make([]string, 0)
ch := make(chan string)

for i := 0; i < totalFiles; i++ {
    wg.Add(1)
    go func() {
        defer wg.Done()
        ch <- createFile()
    }()
}

wg.Wait()

for i := range ch {
    filenames = append(filenames, i)
}

return filenames, nil

谢谢!

第一个有一场比赛。您必须保护对 filenames:

的访问
mu:=sync.Mutex{}
for i := 0; i < totalFiles; i++ {
    wg.Add(1)
    go func() {
        defer wg.Done()
        mu.Lock()
        defer mu.Unlock()
        filenames = append(filenames, createFile())
    }()
}

对于第二种情况,您正在等待 goroutines 完成,但是 goroutines 只能在您从通道读取后才能完成,所以死锁。您可以通过在单独的 goroutine 中读取频道来修复它。

go func() {
  for i := range ch {
      filenames = append(filenames, i)
  }
}()

wg.Wait()
close(ch) // Required, so the goroutine can terminate

return filenames, nil

有lock-free版本,如果文件数固定:

filenames := make([]string, totalFiles)
for i := 0; i < totalFiles; i++ {
    wg.Add(1)
    go func(index int) {
        defer wg.Done()
        filenames[index]=createFile()
    }(i)
}
wg.Wait()