2017-05-25 128 views
1

concurrent.go:如何在不使用sync.WaitGroup的情况下防止死锁?

package main 

import (
    "fmt" 
    "sync" 
) 

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

// WORKERS represents the number of 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) 
    } 

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

实施例是here上goplay。

+1

不能使用范围,而最终关闭通道,你可以不等待关闭通道。你试图完成什么? (顺便说一句,WaitGroup不存在的时候,我们只是手动计算来自通道的令牌,但在更多代码中它仍然是相同的概念) – JimB

+0

您不需要互斥或原子计数器,这是等待的标准方式WaitGroup之前的goroutines计算通过通道返回的值。 – JimB

+0

这取决于具体情况,但您在自己的代码中有一个注释示例。直到达到您分派的JOBS数量为止,您会收到结果数量。 – JimB

回答

-1

这是在没有waitgroup的情况下同步的示例的证明。

Example in the Go playground

package main 

import (
    "fmt" 
) 

// number of jobs workers do 
const JOBS = 10 

// number of workers 
const WORKERS = 2 

func work(in <-chan int, out chan<- int, done chan<- bool) { 
    for n := range in { 
     out <- n * n 
    } 
    done <- true 
} 

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

    // launch workers 
    for w := 1; w <= WORKERS; w++ { 
     go work(in, out, done) 
    } 

    // give jobs to workers 
    for j := 1; j <= JOBS; j++ { 
     in <- j 
    } 
    close(in) 

    // list the results 
    go func() { 
     i := 0 
     for r := range out { 
      fmt.Println("result:", r) 

      // when all jobs completed mark as done 
      i++ 
      if i == JOBS { 
       done <- true 
      } 
     } 
    }() 

    // wait for all goroutines to keep up 
    // WORKERS + RESULT go routines 
    for i := 0; i < WORKERS + 1; i++ { 
     <- done 
    } 

    // prevent leaking chans 
    close(out) 
} 
+1

从来就不是一个好例子。 – JimB

+1

@inanc我们推荐使用'WaitGroup'。严重的是,你对'WaitGroup'有什么反应? – icza

+1

是的,为了具有并发性,您需要同步事物。不幸的是,你需要考虑同步,你不能免费获得它。您的例程不能保证可执行,即如果内核不会给您一秒的CPU时间,则可能在打印任何结果之前退出。 – JimB

3

Goroutines同时独立运行。 Spec: Go statements:

A“走出去”的语句开头的函数调用的控制,或够程独立并发线程的执行,同样的地址空间内。

如果你想使用for rangeout通道接收值,这意味着out通道只能关闭一次全部够程完成后发送就可以了。

由于goroutines同时独立运行,没有同步,你不能拥有这个。

使用WaitGroup是一个意思,一种方法(确保我们在关闭out之前等待所有goroutines完成工作)。

您的注释代码是另一种方式:注释代码从通道接收的许多值与许多应用程序应该发送的值一样多,这只有在所有的goroutine都发送它们的值时才有可能。同步是发送语句和接收操作。

注:

从信道一般接收结果异步地完成的,在专用的goroutine,或使用甚至多个够程。这样做不需要使用缓冲区能够缓冲所有结果的通道。您仍需要同步才能等待所有工作人员完成工作,但由于gorutine调度和执行的并行和独立性质,您无法避免这种情况。

+0

_不同的角度:_在[对于语句文档](https://golang.org/ref/spec#For_statements)写道:_'如果频道是零,范围表达式永远阻止。'但是:当我将' nil'到'out','range out'不会阻塞,并且会死锁('out out'在'range out'之前'out = nil')。为什么? –

+0

@inanc我不知道你尝试过的代码,但是:死锁=所有goroutines被阻塞,所以它暗示'range out'也被阻塞。如果你将'nil'指定为'out',然后尝试对其进行“range”操作,那么主要的goroutine将会阻塞,没有其他goutoutine可以挽救它,并且从工作人员完成时(或何时)完成:死锁。 – icza

+0

我明白了。我尝试过[这里](https://play.golang.org/p/WUDyMa6XtV)。它没有'WaitGroup'。这没有死锁,因为'main'仍然可以独立于'range out'运行。使用睡眠进行同步的 –

相关问题