Przykład wycieku goroutine i sposób jego debugowania

Kiedy podchodzę do funkcji, która łączy uruchamianie fragmentu kodu w goroutine i komunikację / anulowanie za pomocą kanałów, zwykle kusi mnie, aby spojrzeć głębiej, ponieważ jest to świetne miejsce do łatwego wprowadzenia wycieku goroutine i te błędy są dość łatwe do usunięcia tęsknię nawet za początkującym programistą Golang. I to właśnie zrobiłem dla tego fragmentu kodu, który znalazłem w KUDO.

Co to jest wyciek goroutine?

Wyciek goroutine jest w zasadzie rodzajem wycieku pamięci. Zaczynasz goroutine, ale to nigdy się nie skończy, zajmując na zawsze pamięć, którą zarezerwował. Aby uprościć nieco przykład, który opublikowałem z KUDO, jest to przykład tego, jak można wprowadzić wyciek goroutine do ich projektu.

To, co robi kod, to wymuszone przekroczenie limitu czasu i okresowa operacja wykonywana wewnątrz goroutine. Każdemu tikowi staramy się zweryfikować logikę biznesową (na przykład upewnić się, że coś jest zdrowe / gotowe) - w tym przykładzie po prostu symuluje to połączenie przez uśpienie, a następnie powrót.

Więc gdzie jest wyciek? W powyższym uproszczonym przykładzie limit czasu wynosi zaledwie 1 sekundę, podczas gdy operacja weryfikacji zajmuje 10 sekund. Oznacza to, że termin byłby egzekwowany jako pierwszy, powracając z „waitReady”. Około 9 sekund później nasz goroutine otrzymuje wynik od weryfikatora i próbuje zapisać w doneChan. Zapis do niebuforowanego kanału jest blokowany i nikt nie słucha na tym kanale, ponieważ już wróciliśmy z waitReady - i oto nasz wyciek!

Jak dowiedzieć się, czy masz wyciek goroutine?

Ogólnie rzecz biorąc, pakiet `runtime` jest tutaj twoim przyjacielem. Jednym ze sposobów jest użycie runtime.NumGoroutine () w teście przed i po wywołaniu funkcji waitReady. Jeśli liczba goroutyn przed oczekiwaniem Gotowy i później nie jest taka sama, masz wyciek.

Inną opcją jest użycie biblioteki Uber - goleak. Jeśli zagłębisz się w sposób, w jaki ten jest implementowany, opiera się on również na pakiecie wykonawczym, tym razem odczytuje wszystkie stosy (funkcja runtime.Stack) i wprowadza kilka metod wygody.