Golang:新手 - Master-Worker并发

时间:2013-12-08 15:27:52

标签: go

我试图实现这个问题(所有goroutines都睡着了 - 死锁!) 这是代码的要点:

var workers = runtime.NumCPU()

func main() {
    jobs := make(chan *myStruct, workers)
    done := make(chan *myStruct, workers)

    go produceWork(file_with_jobs, jobs)
    for i := 0; i < runtime.NumCPU(); i++ {
        go Worker(jobs, done)
    }
    consumeWork(done)
}

func produceWork(vf string, jobs chan *utils.DigSigEntries) {
    defer close(jobs)

    // load file with jobs
    file, err := ini.LoadFile(vf)

    // get data for processing
    for data, _ := range file {
        // ...
        jobs <- &myStruct{data1, data2, data3, false}
    }
}

func Worker(in, out chan *myStruct) {
    for {
        item, open := <-in
        if !open {
            break
        }

        process(item)
        out <- item
    }
    // close(out)   --> tried closing the out channel, but then not all items are processed
    //                  though no panics occur.
}

func process(item *myStruct) {
    //...modify the item
    item.status = true
}

func consumeWork(done chan *myStruct) {
    for val := range done {
        if !val.status {
            fmt.Println(val)
        }
    }
}

我主要是试图了解如何在不使用同步/等待的情况下实现这一点 - 只是纯粹的频道 - 这可能吗?这个例程的目标是让一个生产者加载由N个工人处理的项目 - 感谢任何指针/帮助。

2 个答案:

答案 0 :(得分:0)

正如siritinga建议的那样,您可以使用第三个信令或计数器通道,例如signal chan boolean,其中produceWork goroutine会在每个作业输入jobs频道之前添加一个值。因此,相同数量的值将传递给signal jobs

func produceWork(vf string, jobs chan *utils.DigSigEntries, signal chan boolean) {
    defer close(jobs)

    // load file with jobs
    file, err := ini.LoadFile(vf)

    // get data for processing
    for data, _ := range file {
        // ...
        signal <- true
        jobs <- &myStruct{data1, data2, data3, false}
    }

    close(signal)
}

然后通过从signal频道读取来开始消费。如果存在值,则可以确定将从out通道读取值(一旦工作人员通过它)。如果signal已关闭,则全部完成。我们可以关闭剩余的done频道:

func consumeWork(done chan *myStruct, signal chan boolean) {
    for _ := range signal {
        val <- done
        if !val.status {
            fmt.Println(val)
        }
    }

    close(done)
} 

虽然这是可能的,但我不会真的推荐它。与使用sync.WaitGroup时相比,它不会使代码更清晰。毕竟,signal频道基本上只能用作计数器。 WaitGroup具有相同的目的,并且会更便宜。

但你的问题不在于如何解决问题,而在于是否可以用纯粹的渠道来解决问题。

答案 1 :(得分:0)

抱歉,我没注意到你想跳过/同步:/ 我会留下答案,也许有人正在寻找这个。

import (
    "sync"
)


func main() {
    jobs := make(chan *myStruct, workers)
    done := make(chan *myStruct, workers)

    var workerWg sync.WaitGroup   // waitGroup for workers
    var consumeWg sync.WaitGroup  // waitGroup for consumer
    consumeWg.Add(1) // add one active Consumer

    for i := 0; i < runtime.NumCPU(); i++ {
        go Worker(&workerWg, jobs, done)
        workerWg.Add(1)
    }
    go consumeWork(&consumeWg, done)

    produceWork(file_with_jobs, jobs)

    close(jobs)
    workerWg.Wait()

    close(done)
    consumeWg.Wait()

}

func produceWork(vf string, jobs chan *utils.DigSigEntries) {

    // load file with jobs
    file, err := ini.LoadFile(vf)

    // get data for processing
    for data, _ := range file {
        // ...
        jobs <- &myStruct{data1, data2, data3, false}
    }
}


func Worker(wg *sync.WaitGroup, done chan *myStruct) {

    defer wg.Done()
    for job := range jobs {

        result := process(job)
        out <- result
    }

   // close(out)   --> tried closing the out channel, but then not all items are processed
   //                  though no panics occur.
}

func process(item *myStruct) {
    //...modify the item
    item.status = true
}

func consumeWork(wg *sync.WaitGroup, done chan *myStruct) {
    defer wg.Done()
    for val := range done {
        if !val.status {
            fmt.Println(val)
        }
    }
}