2017-05-25 42 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'運行。使用睡眠進行同步的 –

相關問題