2013-05-16 14 views
7

Muszę rozpocząć wielką ilość goroutines i czekać na ich zakończenie. Intuicyjny sposób wydaje się używać kanału czekać, aż wszystkie z nich są wykończone:Poczekaj na zakończenie g goroutines

package main 

type Object struct { 
    //data 
} 

func (obj *Object) Update(channel chan int) { 
    //update data 
    channel <- 1 
    return 
} 

func main() { 

    channel := make(chan int, n) 
    list := make([]Object, n, m) 
    for { 
     for _, object := range list { 
      go object.Update(channel) 
     } 
     for i := 0; i < n; i++ { 
      <-channel 
     } 
     //now everything has been updated. start again 
    } 
} 

Ale problemem jest to, że ilość przedmiotów, a zatem kwota goroutines może się zmienić. Czy można zmienić rozmiar bufora kanału?

Czy może być bardziej elegancki sposób na zrobienie tego?

+2

Możesz dokonać ponownego przydzielenia każdej iteracji, ale możesz zajrzeć do [WaitGroup] (http://golang.org/pkg/sync/#WaitGroup). – tjameson

+0

tjameson, dzięki za szybką pomoc. To wygląda naprawdę dobrze. Możesz chcieć, aby była to odpowiedź. – lhk

+0

Zrobione, z przykładem = D – tjameson

Odpowiedz

27

Użyłem WaitGroup jako rozwiązania tego problemu. Tłumaczenia swój aktualny kod, a niektóre dzienniki, aby było jasne, co się dzieje:

package main 

import "sync" 
import "fmt" 
import "time" 

type Object struct { 
    //data 
} 

func (obj *Object) Update(wg *sync.WaitGroup) { 
    //update data 
    time.Sleep(time.Second) 
    fmt.Println("Update done") 
    wg.Done() 
    return 
} 

func main() { 
    var wg sync.WaitGroup 
    list := make([]Object, 5) 
    for { 
     for _, object := range list { 
      wg.Add(1) 
      go object.Update(&wg) 
     } 
     //now everything has been updated. start again 
     wg.Wait() 
     fmt.Println("Group done") 
    } 
} 
+8

Dobra odpowiedź! Prawdopodobnie wstawiłbym 'defer wg.Done()' na początku 'Update' chociaż na wypadek, gdyby funkcja ta rosła i zyskiwała na wczesnym etapie w przyszłości. –

+0

Lub na wypadek paniki lub czegoś. – tjameson

4

To zadanie nie jest całkiem banalne, całkiem łatwo jest napisać coś niedozwolonego. Zalecam użycie gotowego rozwiązania w stdlib - sync.WaitGroup. Cytując z linku:

A WaitGroup czeka na zbieranie goroutin, aby zakończyć. Główna goroutyna nazywa się Add, aby ustawić liczbę goroutin, na które należy czekać. Następnie każdy z goroutines działa i wywołuje Gotowe, kiedy skończy. W tym samym czasie, Wait może być użyty do zablokowania aż do zakończenia wszystkich goroutinów.

+0

A jeśli liczba goroutinesów na które trzeba czekać nie jest znana z góry? – Dfr

+0

@Def zwiększaj licznik, gdy uruchamiasz każdą goroutine, więc to rozwiązanie jest nadal najlepszym rozwiązaniem, gdy nie znasz liczby goroutines, które będziesz uruchamiać. – Awn

0

@tjameson świetną robotę wyjaśniający, jak korzystać WaitGroup, jak przekazać referencję do obiektu WaitGroup do swojej funkcji. Jedną zmianą, jaką wprowadziłbym do jego przykładu, jest dźwignia defer, gdy jesteś Done. Myślę, że to defer ws.Done() powinno być pierwszym stwierdzeniem w twojej funkcji.

Podoba mi się prostota: WaitGroup. Nie podoba mi się jednak, że musimy przekazać odniesienie do goroutine, ponieważ oznaczałoby to, że logika współbieżności byłaby mieszana z logiką biznesową.

Więc wymyśliłem ten rodzajowy funkcji, aby rozwiązać ten problem dla mnie:

// Parallelize parallelizes the function calls 
func Parallelize(functions ...func()) { 
    var waitGroup sync.WaitGroup 
    waitGroup.Add(len(functions)) 

    defer waitGroup.Wait() 

    for _, function := range functions { 
     go func(copy func()) { 
      defer waitGroup.Done() 
      copy() 
     }(function) 
    } 
} 

Więc Przykładem może być rozwiązany w ten sposób:

type Object struct { 
    //data 
} 

func (obj *Object) Update() { 
    //update data 
    time.Sleep(time.Second) 
    fmt.Println("Update done") 
    return 
} 

func main() { 
    functions := []func(){} 
    list := make([]Object, 5) 
    for _, object := range list { 
     function := func(obj Object){ object.Update() }(object) 
     functions = append(functions, function) 
    } 

    Parallelize(functions...)   

    fmt.Println("Group done") 
} 

Jeśli chcesz go używać, możesz go znaleźć tutaj: https://github.com/shomali11/util