3 回答

TA貢獻2037條經(jīng)驗 獲得超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條經(jīng)驗 獲得超10個贊
請我首先澄清您的術(shù)語:對渠道末端的誤解可能會導(dǎo)致以后出現(xiàn)問題。您詢問“輸出通道”和“輸入通道”。哪有這回事; 只有渠道。
每個通道都有兩端:輸出(寫入)端和輸入(讀取)端。我會假設(shè)這就是你的意思。
現(xiàn)在回答你的問題。
以最簡單的情況為例:您只有一個發(fā)送方 goroutine 寫入通道,并且只有一個工作 goroutine 從另一端讀取,并且通道的緩沖為零。發(fā)送方 goroutine 將在寫入每個項目時阻塞,直到該項目被消耗。通常,這在第一次發(fā)生時很快。一旦第一個項目傳遞給工作人員,工作人員就會很忙,發(fā)件人必須等待才能傳遞第二項內(nèi)容。因此,乒乓效應(yīng)如下:作者或讀者會忙,但不會兩者都忙。在 Rob Pike 所描述的意義上,goroutine 將是并發(fā)的,但實際上并不總是并行執(zhí)行。
如果您有許多從通道讀取的工作程序 goroutine(并且其輸入端由所有人共享),則發(fā)送方最初可以將一個項目分發(fā)給每個工作程序,但隨后必須等待它們工作(類似于上面描述的乒乓球案例)。最后,當(dāng)所有項目都由發(fā)送方發(fā)送后,它的工作就完成了。然而,讀者可能還沒有完成他們的工作。有時我們關(guān)心發(fā)件人是否提前完成,有時我們不關(guān)心。知道何時發(fā)生這種情況最容易通過 WaitGroup 完成(參見Not_a_Golfer的回答和我對相關(guān)問題的回答)。
還有一個稍微復(fù)雜一點的替代方法:您可以使用返回通道來完成信號傳輸,而不是使用WaitGroup. 這并不難做到,但WaitGroup在這種情況下是首選,更簡單。
相反,如果通道包含緩沖區(qū),則發(fā)送方發(fā)送最后一項的時間點會更快發(fā)生。在通道每個 worker 有一個緩沖區(qū)空間的極限情況下;這將允許發(fā)件人非常快速地完成,然后,可能繼續(xù)處理其他事情。(任何比這更多的緩沖都是浪費)。
發(fā)送者的這種解耦允許完全異步的行為模式,深受使用其他技術(shù)堆棧(Node-JS 和 JVM 的人)的人的喜愛。與它們不同的是,Go不需要您這樣做,但您可以選擇。
早在 90 年代初期,作為批量同步并行 (BSP) 策略工作的副作用,Leslie Valiant 證明有時非常簡單的同步策略可能很便宜。關(guān)鍵因素是需要足夠的并行松弛(也稱為過度并行)來保持處理器內(nèi)核忙碌。這意味著必須有足夠多的其他工作要做,以便任何特定的 goroutine 被阻塞一段時間都無關(guān)緊要。
奇怪的是,這可能意味著使用較少數(shù)量的 goroutine 可能比使用較大數(shù)量的 goroutine 需要更多的小心。
了解過度并行的影響是有用的:如果整個網(wǎng)絡(luò)具有過度并行,通常沒有必要付出額外的努力來使所有內(nèi)容異步,因為無論哪種方式,CPU 內(nèi)核都會很忙。
因此,雖然知道如何等待發(fā)件人完成很有用,但更大的應(yīng)用程序可能不需要您以同樣的方式關(guān)注。
作為最后一個腳注,WaitGroup是BSP 中使用的意義上的障礙。通過結(jié)合障礙和渠道,您可以同時使用 BSP 和 CSP。

TA貢獻1847條經(jīng)驗 獲得超7個贊
我個人喜歡為此使用 a sync.WaitGroup。等待組是一個同步計數(shù)器,它具有三種方法 - Wait()、Done()和Add()。您要做的是增加等待組的計數(shù)器,將其傳遞給工作人員,并讓Done()他們在完成后調(diào)用。然后,您只需阻塞另一端的等待組并在它們?nèi)客瓿珊箨P(guān)閉輸出通道,從而導(dǎo)致輸出處理器退出。
基本上:
// 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 關(guān)注
- 182 瀏覽
添加回答
舉報