3 回答
TA貢獻2037條經驗 獲得超6個贊
var Z = "Z"
func Loop() {
sc := make(chan *string)
ss := make([]string, 0)
done := make(chan struct{}, 1)
go func() {
//1 QUERY
slice1 := []string{"a", "b", "c"}
//2 WG INIT
var wg1 sync.WaitGroup
wg1.Add(len(slice1))
//3 LOOP->
loopSlice1(slice1, sc, &wg1)
//7 WG WAIT<-
wg1.Wait()
sc <- &Z
done <- struct{}{}
}()
go func() {
var cc *string
for {
cc = <-sc
log.Infof("<-sc %s", *cc)
if *cc == Z {
break
}
ss = append(ss, *cc)
}
}()
<-done
log.Infof("FUN: %#v", ss)
}
func loopSlice1(slice1 []string, sc chan *string, wg1 *sync.WaitGroup) {
for i, x := range slice1 {
//4 GO
go func(n int, v string) {
//5 WG DONE
defer wg1.Done()
//6 DOING
//[1 QUERY
slice2 := []string{"X", "Y", "Z"}
//[2 WG INIT
var wg2 sync.WaitGroup
wg2.Add(len(slice2))
//[3 LOOP ->
loopSlice2(n, v, slice2, sc, &wg2)
//[7 WG WAIT <-
wg2.Wait()
}(i, x)
}
}
func loopSlice2(n1 int, v1 string, slice2 []string, sc chan *string, wg2 *sync.WaitGroup) {
for j, y := range slice2 {
//[4 GO
go func(n2 int, v2 string) {
//[5 WG DONE
defer wg2.Done()
//[6 DOING
r := fmt.Sprintf("%v%v %v,%v", n1, n2, v1, v2)
sc <- &r
}(j, y)
}
}
TA貢獻1777條經驗 獲得超10個贊
請我首先澄清您的術語:對渠道末端的誤解可能會導致以后出現問題。您詢問“輸出通道”和“輸入通道”。哪有這回事; 只有渠道。
每個通道都有兩端:輸出(寫入)端和輸入(讀?。┒?。我會假設這就是你的意思。
現在回答你的問題。
以最簡單的情況為例:您只有一個發送方 goroutine 寫入通道,并且只有一個工作 goroutine 從另一端讀取,并且通道的緩沖為零。發送方 goroutine 將在寫入每個項目時阻塞,直到該項目被消耗。通常,這在第一次發生時很快。一旦第一個項目傳遞給工作人員,工作人員就會很忙,發件人必須等待才能傳遞第二項內容。因此,乒乓效應如下:作者或讀者會忙,但不會兩者都忙。在 Rob Pike 所描述的意義上,goroutine 將是并發的,但實際上并不總是并行執行。
如果您有許多從通道讀取的工作程序 goroutine(并且其輸入端由所有人共享),則發送方最初可以將一個項目分發給每個工作程序,但隨后必須等待它們工作(類似于上面描述的乒乓球案例)。最后,當所有項目都由發送方發送后,它的工作就完成了。然而,讀者可能還沒有完成他們的工作。有時我們關心發件人是否提前完成,有時我們不關心。知道何時發生這種情況最容易通過 WaitGroup 完成(參見Not_a_Golfer的回答和我對相關問題的回答)。
還有一個稍微復雜一點的替代方法:您可以使用返回通道來完成信號傳輸,而不是使用WaitGroup. 這并不難做到,但WaitGroup在這種情況下是首選,更簡單。
相反,如果通道包含緩沖區,則發送方發送最后一項的時間點會更快發生。在通道每個 worker 有一個緩沖區空間的極限情況下;這將允許發件人非??焖俚赝瓿?,然后,可能繼續處理其他事情。(任何比這更多的緩沖都是浪費)。
發送者的這種解耦允許完全異步的行為模式,深受使用其他技術堆棧(Node-JS 和 JVM 的人)的人的喜愛。與它們不同的是,Go不需要您這樣做,但您可以選擇。
早在 90 年代初期,作為批量同步并行 (BSP) 策略工作的副作用,Leslie Valiant 證明有時非常簡單的同步策略可能很便宜。關鍵因素是需要足夠的并行松弛(也稱為過度并行)來保持處理器內核忙碌。這意味著必須有足夠多的其他工作要做,以便任何特定的 goroutine 被阻塞一段時間都無關緊要。
奇怪的是,這可能意味著使用較少數量的 goroutine 可能比使用較大數量的 goroutine 需要更多的小心。
了解過度并行的影響是有用的:如果整個網絡具有過度并行,通常沒有必要付出額外的努力來使所有內容異步,因為無論哪種方式,CPU 內核都會很忙。
因此,雖然知道如何等待發件人完成很有用,但更大的應用程序可能不需要您以同樣的方式關注。
作為最后一個腳注,WaitGroup是BSP 中使用的意義上的障礙。通過結合障礙和渠道,您可以同時使用 BSP 和 CSP。
TA貢獻1847條經驗 獲得超7個贊
我個人喜歡為此使用 a sync.WaitGroup。等待組是一個同步計數器,它具有三種方法 - Wait()、Done()和Add()。您要做的是增加等待組的計數器,將其傳遞給工作人員,并讓Done()他們在完成后調用。然后,您只需阻塞另一端的等待組并在它們全部完成后關閉輸出通道,從而導致輸出處理器退出。
基本上:
// create the wait group
wg := sync.WaitGroup{}
// this is the output channel
outchan := make(chan whatever)
// start the workers
for i := 0; i < N; i++ {
wg.Add(1) //we increment by one the waitgroup's count
//the worker pushes data onto the output channel and calls wg.Done() when done
go work(&wg, outchan)
}
// this is our "waiter" - it blocks until all workers are done and closes the channel
go func() {
wg.Wait()
close(outchan)
}()
//this loop will exit automatically when outchan is closed
for item := range outchan {
workWithIt(item)
}
// TADA!
- 3 回答
- 0 關注
- 197 瀏覽
添加回答
舉報
