How to prevent deadlocks without using sync.WaitGroup?

huangapple go评论77阅读模式
英文:

How to prevent deadlocks without using sync.WaitGroup?

问题

concurrent.go:

package main

import (
	"fmt"
	"sync"
)

// JOBS 表示工作任务的数量
const JOBS = 2

// WORKERS 表示工作者的数量
const WORKERS = 5

func work(in <-chan int, out chan<- int, wg *sync.WaitGroup) {
	for n := range in {
		out <- n * n
	}
	wg.Done()
}

var wg sync.WaitGroup

func main() {
	in := make(chan int, JOBS)
	out := make(chan int, JOBS)

	for w := 1; w <= WORKERS; w++ {
		wg.Add(1)
		go work(in, out, &wg)
	}

	for j := 1; j <= JOBS; j++ {
		in <- j
	}
	close(in)

	wg.Wait()
	close(out)
	for r := range out {
		fmt.Println("result:", r)
	}

	// 这是一种解决方案,但我想使用 `range out` 来实现,并且不使用 WaitGroups
	// for r := 1; r <= JOBS; r++ {
	// 	fmt.Println("result:", <-out)
	// }
}

你可以在goplay上查看示例。

英文:

concurrent.go:

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
)

// JOBS represents the number of jobs workers do
const JOBS = 2

// WORKERS represents the number of workers
const WORKERS = 5

func work(in &lt;-chan int, out chan&lt;- int, wg *sync.WaitGroup) {
	for n := range in {
		out &lt;- n * n
	}
	wg.Done()
}

var wg sync.WaitGroup

func main() {
	in := make(chan int, JOBS)
	out := make(chan int, JOBS)

	for w := 1; w &lt;= WORKERS; w++ {
		wg.Add(1)
		go work(in, out, &amp;wg)
	}

	for j := 1; j &lt;= JOBS; j++ {
		in &lt;- j
	}
	close(in)

	wg.Wait()
	close(out)
	for r := range out {
		fmt.Println(&quot;result:&quot;, r)
	}

	// This is a solution but I want to do it with `range out`
    // and also without WaitGroups
	// for r := 1; r &lt;= JOBS; r++ {
	// 	fmt.Println(&quot;result:&quot;, &lt;-out)
	// }
}

Example is here on goplay.

答案1

得分: 3

Goroutines并发且独立地运行。

如果你想使用for rangeout通道接收值,那么意味着只有在所有的goroutine都发送完毕后,out通道才能关闭。

由于goroutine并发且独立地运行,没有同步的话,你无法做到这一点。

使用WaitGroup是一种方法,一种在关闭out之前确保等待所有goroutine完成任务的方式。

你的注释代码是另一种方式:注释代码从通道接收的值的数量正好等于goroutine应该在通道上发送的值的数量,只有在所有的goroutine都发送了它们的值时才可能实现这一点。发送语句和接收操作是同步的。

注意:

通常,从通道接收结果是异步完成的,可以在专用的goroutine中完成,或者甚至使用多个goroutine。这样做的话,你不需要使用具有足够缓冲区来缓冲所有结果的通道。你仍然需要同步等待所有的工作线程完成任务,由于goroutine调度和执行的并发和独立性,你无法避免这一点。

英文:

Goroutines run concurrently and independently. Spec: Go statements:

> A "go" statement starts the execution of a function call as an independent concurrent thread of control, or goroutine, within the same address space.

If you want to use for range to receive values from the out channel, that means the out channel can only be closed once all goroutines are done sending on it.

Since goroutines run concurrently and independently, without synchronization you can't have this.

Using WaitGroup is one mean, one way to do it (to ensure we wait all goroutines to do their job before closing out).

Your commented code is another way of that: the commented code receives exactly as many values from the channel as many the goroutines ought to send on it, which is only possible if all goroutines do send their values. The synchronization are the send statements and receive operations.

Notes:

Usually receiving results from the channel is done asynchronously, in a dedicated goroutine, or using even multiple goroutines. Doing so you are not required to use channels with buffers capable of buffering all the results. You will still need synchronization to wait for all workers to finish their job, you can't avoid this due to the concurrent and independent nature of gorutine scheduling and execution.

huangapple
  • 本文由 发表于 2017年5月25日 19:28:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/44179480.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定