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

时间:2017-05-25 11:28:57

标签: go concurrency channel

concurrent.go:

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)
    // }
}

示例是goplay上的here

2 个答案:

答案 0 :(得分:3)

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

  

“go”语句在同一地址空间内作为独立并发控制线程 goroutine 开始执行函数调用。

如果您想使用for rangeout频道接收值,则表示out频道只有在所有goroutine完成发送后才能关闭。

由于goroutine并发且独立地运行,没有同步,你就不能拥有它。

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

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

备注:

通常从通道接收结果是异步完成的,在专用的goroutine中,或者甚至使用多个goroutine。这样做不需要使用具有缓冲能力缓冲所有结果的通道。你仍然需要同步来等待所有工人完成他们的工作,由于gorutine调度和执行的并发和独立性,你无法避免这种情况。

答案 1 :(得分:-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
            if i++; 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)
}