3 回答

TA貢獻(xiàn)1966條經(jīng)驗(yàn) 獲得超4個(gè)贊
獨(dú)立于第二個(gè)示例的正確性(如評(píng)論中所述,您沒(méi)有按照自己的想法行事,但很容易修復(fù)),我傾向于認(rèn)為第一個(gè)示例更容易掌握。
現(xiàn)在,我什至不會(huì)說(shuō)頻道更慣用了。通道是 Go 語(yǔ)言的一個(gè)標(biāo)志性特性,并不意味著盡可能地使用它們是慣用的。Go 中的慣用語(yǔ)是使用最簡(jiǎn)單、最容易理解的解決方案:在這里,WaitGroup
傳遞含義(您的主要功能是Wait
讓工人完成)和機(jī)制(工人通知他們何時(shí)完成Done
)。
除非您處于非常特殊的情況,否則我不建議在此處使用通道解決方案。

TA貢獻(xiàn)1811條經(jīng)驗(yàn) 獲得超6個(gè)贊
這取決于用例。如果您要分派一次性作業(yè)以并行運(yùn)行而無(wú)需知道每個(gè)作業(yè)的結(jié)果,那么您可以使用WaitGroup
. 但是如果你需要從 goroutines 收集結(jié)果,那么你應(yīng)該使用一個(gè)通道。
由于通道雙向工作,我?guī)缀蹩偸鞘褂猛ǖ馈?/p>
另一方面,正如評(píng)論中所指出的,您的頻道示例未正確實(shí)現(xiàn)。您需要一個(gè)單獨(dú)的通道來(lái)指示沒(méi)有更多的工作要做(一個(gè)例子是這里)。在您的情況下,由于您事先知道字?jǐn)?shù),因此您可以只使用一個(gè)緩沖通道并接收固定次數(shù)以避免聲明關(guān)閉通道。

TA貢獻(xiàn)1812條經(jīng)驗(yàn) 獲得超5個(gè)贊
對(duì)于您的簡(jiǎn)單示例(表示作業(yè)完成),這WaitGroup是顯而易見(jiàn)的選擇。Go 編譯器非常友好,不會(huì)怪你使用通道來(lái)發(fā)送簡(jiǎn)單的完成任務(wù)信號(hào),但一些代碼審查者會(huì)這樣做。
"WaitGroup 等待一組 goroutines 完成。主 goroutine 調(diào)用Add(n)設(shè)置要等待的 goroutines 的數(shù)量。然后每個(gè) goroutines 運(yùn)行并Done()在完成時(shí)調(diào)用。同時(shí),Wait 可用于阻塞直到所有g(shù)oroutine 已經(jīng)完成了?!?/p>
words := []string{"foo", "bar", "baz"}
var wg sync.WaitGroup
for _, word := range words {
wg.Add(1)
go func(word string) {
defer wg.Done()
time.Sleep(100 * time.Millisecond) // a job
fmt.Println(word)
}(word)
}
wg.Wait()
可能性僅受您的想象力的限制:
通道可以被緩沖:
words := []string{"foo", "bar", "baz"}
done := make(chan struct{}, len(words))
for _, word := range words {
go func(word string) {
time.Sleep(100 * time.Millisecond) // a job
fmt.Println(word)
done <- struct{}{} // not blocking
}(word)
}
for range words {
<-done
}
通道可以是無(wú)緩沖的,你可以只使用一個(gè)信號(hào)通道(例如chan struct{}):
words := []string{"foo", "bar", "baz"}
done := make(chan struct{})
for _, word := range words {
go func(word string) {
time.Sleep(100 * time.Millisecond) // a job
fmt.Println(word)
done <- struct{}{} // blocking
}(word)
}
for range words {
<-done
}
您可以限制具有緩沖通道容量的并發(fā)作業(yè)數(shù):
t0 := time.Now()
var wg sync.WaitGroup
words := []string{"foo", "bar", "baz"}
done := make(chan struct{}, 1) // set the number of concurrent job here
for _, word := range words {
wg.Add(1)
go func(word string) {
done <- struct{}{}
time.Sleep(100 * time.Millisecond) // job
fmt.Println(word, time.Since(t0))
<-done
wg.Done()
}(word)
}
wg.Wait()
您可以使用頻道發(fā)送消息:
done := make(chan string)
go func() {
for _, word := range []string{"foo", "bar", "baz"} {
done <- word
}
close(done)
}()
for word := range done {
fmt.Println(word)
}
基準(zhǔn):
go test -benchmem -bench . -args -n 0
# BenchmarkEvenWaitgroup-8 1827517 652 ns/op 0 B/op 0 allocs/op
# BenchmarkEvenChannel-8 1000000 2373 ns/op 520 B/op 1 allocs/op
go test -benchmem -bench .
# BenchmarkEvenWaitgroup-8 1770260 678 ns/op 0 B/op 0 allocs/op
# BenchmarkEvenChannel-8 1560124 1249 ns/op 158 B/op 0 allocs/op
代碼( main_test.go):
package main
import (
"flag"
"fmt"
"os"
"sync"
"testing"
)
func BenchmarkEvenWaitgroup(b *testing.B) {
evenWaitgroup(b.N)
}
func BenchmarkEvenChannel(b *testing.B) {
evenChannel(b.N)
}
func evenWaitgroup(n int) {
if n%2 == 1 { // make it even:
n++
}
for i := 0; i < n; i++ {
wg.Add(1)
go func(n int) {
select {
case ch <- n: // tx if channel is empty
case i := <-ch: // rx if channel is not empty
// fmt.Println(n, i)
_ = i
}
wg.Done()
}(i)
}
wg.Wait()
}
func evenChannel(n int) {
if n%2 == 1 { // make it even:
n++
}
for i := 0; i < n; i++ {
go func(n int) {
select {
case ch <- n: // tx if channel is empty
case i := <-ch: // rx if channel is not empty
// fmt.Println(n, i)
_ = i
}
done <- struct{}{}
}(i)
}
for i := 0; i < n; i++ {
<-done
}
}
func TestMain(m *testing.M) {
var n int // We use TestMain to set up the done channel.
flag.IntVar(&n, "n", 1_000_000, "chan cap")
flag.Parse()
done = make(chan struct{}, n)
fmt.Println("n=", n)
os.Exit(m.Run())
}
var (
done chan struct{}
ch = make(chan int)
wg sync.WaitGroup
)
- 3 回答
- 0 關(guān)注
- 246 瀏覽
添加回答
舉報(bào)