doushui3061
2017-05-25 11:28
浏览 62
已采纳

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

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

Example is here on goplay.

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • doujie3888 2017-05-25 14:25
    已采纳

    This is a proof of example syncing without 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)
    }
    
    打赏 评论
  • donglizuo8892 2017-05-25 12:31

    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.

    打赏 评论

相关推荐 更多相似问题