Ten kod wybiera wszystkie pliki xml w tym samym folderze, ponieważ wywołany plik wykonywalny i asynchronicznie stosuje przetwarzanie do każdego wyniku w metodzie wywołania zwrotnego (w poniższym przykładzie drukowana jest tylko nazwa pliku).
Jak uniknąć używania metody uśpienia, aby główna metoda nie zakończyła się? Mam problemy z owinięciem głowy wokół kanałów (zakładam, że właśnie tego potrzeba, aby zsynchronizować wyniki), więc każda pomoc jest mile widziana!
package main
import (
"fmt"
"io/ioutil"
"path"
"path/filepath"
"os"
"runtime"
"time"
)
func eachFile(extension string, callback func(file string)) {
exeDir := filepath.Dir(os.Args[0])
files, _ := ioutil.ReadDir(exeDir)
for _, f := range files {
fileName := f.Name()
if extension == path.Ext(fileName) {
go callback(fileName)
}
}
}
func main() {
maxProcs := runtime.NumCPU()
runtime.GOMAXPROCS(maxProcs)
eachFile(".xml", func(fileName string) {
// Custom logic goes in here
fmt.Println(fileName)
})
// This is what i want to get rid of
time.Sleep(100 * time.Millisecond)
}
go
synchronization
goroutine
Dante
źródło
źródło
Note that calls with positive delta must happen before the call to Wait, or else Wait may wait for too small a group. Typically this means the calls to Add should execute before the statement creating the goroutine or other event to be waited for. See the WaitGroup example.
wg := new(sync.WaitGroup)
zamiastvar wg sync.WaitGroup
.wg.Add(len(urls))
tuż nad liniąfor _, url := range urls
, uważam, że lepiej jest użyć opcji Dodaj tylko raz.go vet
wykrywa ten przypadek i ostrzega za pomocą„ func przechodzi blokada według wartości : sync.WaitGroup zawiera sync.noCopy ".WaitGroups to zdecydowanie kanoniczny sposób na zrobienie tego. Jednak tylko dla kompletności, oto rozwiązanie, które było powszechnie używane przed wprowadzeniem WaitGroups. Podstawowym pomysłem jest użycie kanału do powiedzenia „Skończyłem” i spowodowanie, aby główny rutynowy czekał, aż każda z nich zgłosi zakończenie.
źródło
doSomething()
zwróci jakiś wynik, to możesz umieścić go na kanale, a możesz zebrać i przetworzyć wyniki w drugiej pętli for (jak tylko będą gotowe)wg.Add(1)
a zatem będzie je śledzić. W przypadku kanałów byłoby to nieco trudniejsze.c
różnią się od głównej gorutyny, z której czytamyc
. W ten sposób główna gorutyna jest zawsze dostępna, aby odczytać wartość z kanału, co nastąpi, gdy jeden z gorutyn będzie dostępny do zapisania wartości w kanale. Masz rację, że gdyby ten kod nie spowodował pojawienia się gorutyn, ale zamiast tego uruchomił wszystko w jednym, to by się zablokował.sync.WaitGroup może Ci tutaj pomóc.
źródło
Chociaż
sync.waitGroup
(wg) jest kanoniczną drogą do przodu, wymaga wykonania przynajmniej niektórychwg.Add
połączeń przedwg.Wait
wykonaniem wszystkich. Może to być niewykonalne w przypadku prostych rzeczy, takich jak robot sieciowy, w przypadku którego nie znasz wcześniej liczby wywołań rekurencyjnych, a pobranie danych, które je napędzają, zajmuje trochę czasuwg.Add
. W końcu musisz załadować i przeanalizować pierwszą stronę, zanim poznasz rozmiar pierwszej partii stron podrzędnych.Napisałem rozwiązanie przy użyciu kanałów, unikając
waitGroup
w swoim rozwiązaniu ćwiczenia Tour of Go - web crawler . Za każdym razem, gdy uruchamiana jest jedna lub więcej procedur go, wysyłasz numer dochildren
kanału. Za każdym razem, gdy procedura GO ma się zakończyć, wysyłasz1
dodone
kanału. Kiedy suma dzieci równa się sumie wykonanych, skończymy.Moim jedynym problemem jest zakodowany rozmiar
results
kanału, ale jest to (obecne) ograniczenie Go.Pełny kod źródłowy rozwiązania
źródło
Oto rozwiązanie wykorzystujące WaitGroup.
Najpierw zdefiniuj 2 metody narzędziowe:
Następnie zamień wywołanie
callback
:W przypadku wywołania funkcji narzędzia:
Ostatni krok, dodaj tę linię na końcu swojego
main
, zamiast twojegosleep
. Dzięki temu główny wątek będzie czekał na zakończenie wszystkich procedur, zanim program będzie mógł się zatrzymać.źródło