2016-09-29 1 views
2

Ich habe eine Scheibe von ganzen Zahlen, die gleichzeitig manipuliert werden:Warten, bis der gepufferte Kanal (Semaphor) leer ist?

ints := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 

Ich verwende einen gepufferten Kanal als Semaphor, um eine eine obere Grenze von gleichzeitig laufenden Go-Routinen zu haben:

sem := make(chan struct{}, 2) 

for _, i := range ints { 
    // acquire semaphore 
    sem <- struct{}{} 

    // start long running go routine 
    go func(id int, sem chan struct{}) { 
    // do something 

    // release semaphore 
    <- sem 
    }(i, sem) 
} 

Der obige Code funktioniert ziemlich gut, bis die letzten oder letzten zwei Ganzzahlen erreicht sind, weil das Programm endet, bevor diese letzten Routinen beendet sind.

Frage: Wie warte ich, bis der gepufferte Kanal abfließt?

+0

Du musst ein Mutex oder etwas verwenden. Der gepufferte Kanal blockiert, wenn er voll ist, aber es gibt keine Sprachfunktion, die blockiert werden kann, bis sie leer ist. – evanmcdonnal

Antwort

8

Sie können auf diese Weise kein Semaphor (Kanal in diesem Fall) verwenden. Es gibt keine Garantie, dass es keinen Punkt leer bleibt, während du Werte verarbeitest und mehr goroutines verschickst. Das ist in diesem Fall kein Problem, da Sie Arbeit synchron versenden, aber da es keinen rennfreien Weg gibt, die Länge eines Kanals zu überprüfen, gibt es kein Grundelement, um auf die Länge eines Kanals zu warten.

Verwenden Sie einen sync.WaitGroup warten, bis alle goroutines fertig sind

+1

Danke, ich habe auch über eine 'WaitGroup' nachgedacht. Das fühlt sich an wie der richtige Weg! – Kiril

0

Offensichtlich wartet niemand darauf, dass Ihre Go-Routinen abgeschlossen werden. Somit endet das Programm, bevor die letzten 2 Go-Routinen abgeschlossen sind. Sie können eine Arbeitsgruppe verwenden, um auf alle Ihre Go-Routinen zu warten, bevor das Programm beendet wird. Das sagt es besser - https://nathanleclaire.com/blog/2014/02/15/how-to-wait-for-all-goroutines-to-finish-executing-before-continuing/

+0

Danke, ich habe gesucht, wie man das vermeidet, und eine 'WorkGroup' funktioniert perfekt. JimBs Antworten verdeutlichten dies – Kiril

2

Verwenden Sie "Worker Pool", um Ihre Daten zu verarbeiten. Es ist cheeper als Lauf goroutine für jeder int, Speicher für Variablen in ihm zuweisen und so weiter ...

ints := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 

ch := make(chan int) 

var wg sync.WaitGroup 

// run worker pool 
for i := 2; i > 0; i-- { 
    wg.Add(1) 

    go func() { 
     defer wg.Done() 

     for id := range ch { 
      // do something 
      fmt.Println(id) 
     } 
    }() 
} 

// send ints to workers 
for _, i := range ints { 
    ch <- i 
} 

close(ch) 

wg.Wait()