Inną odpowiedzią, w zależności od sytuacji, jest użycie kontekstu Go. Nie mam czasu ani wiedzy, aby odpowiedzieć na to pytanie. Chciałem tylko wspomnieć o tym tutaj, aby ludzie, którzy szukają i uważają tę odpowiedź za niezadowalającą, mają inny wątek do wyciągnięcia (gra słów zamierzona). W większości przypadków należy postępować tak, jak sugeruje zaakceptowana odpowiedź. Ta odpowiedź wspomina o kontekstach: stackoverflow.com/a/47302930/167958
Omnifarious,
Odpowiedzi:
51
EDYCJA: Napisałem tę odpowiedź w pośpiechu, zanim zdałem sobie sprawę, że twoje pytanie dotyczy wysyłania wartości do chan wewnątrz goroutine. Poniższe podejście można zastosować z dodatkowym kanałem, jak zasugerowano powyżej, lub korzystając z faktu, że kanał, który już masz, jest dwukierunkowy, możesz użyć tylko jednego ...
Jeśli twój goroutine istnieje wyłącznie po to, aby przetwarzać elementy wychodzące z kanału, możesz skorzystać z wbudowanego „zamknij” i specjalnego formularza odbioru dla kanałów.
Oznacza to, że kiedy skończysz wysyłać elementy na kanale, zamykasz go. Następnie wewnątrz swojego goroutine otrzymujesz dodatkowy parametr do operatora odbioru, który pokazuje, czy kanał został zamknięty.
Oto pełny przykład (grupa waitgroup służy do upewnienia się, że proces będzie kontynuowany do zakończenia gorutyny):
package mainimport"sync"
func main(){var wg sync.WaitGroup
wg.Add(1)
ch := make(chan int)
go func(){for{
foo, ok :=<- chif!ok {
println("done")
wg.Done()return}
println(foo)}}()
ch <-1
ch <-2
ch <-3
close(ch)
wg.Wait()}
Ciało wewnętrznej gorutyny jest bardziej idiomatycznie napisane za pomocą deferwywołania wg.Done()i range chpętli do iteracji po wszystkich wartościach, aż kanał zostanie zamknięty.
Alan Donovan,
115
Zwykle podaje się goroutine (prawdopodobnie oddzielny) kanał sygnałowy. Ten kanał sygnałowy jest używany do wpychania wartości, kiedy chcesz, aby goroutine się zatrzymał. Sondaże goroutine, które regularnie przekazują kanał. Gdy tylko wykryje sygnał, kończy pracę.
quit := make(chan bool)
go func(){for{select{case<- quit:returndefault:// Do other stuff}}}()// Do stuff// Quit goroutine
quit <-true
Nie wystarczająco dobre. Co jeśli goroutine utknie w nieskończonej pętli z powodu błędu?
Elazar Leibovich
232
Następnie błąd powinien zostać naprawiony.
jimt
13
Elazar, sugerujesz sposób na zatrzymanie funkcji po jej wywołaniu. Goroutine to nie nić. Może działać w innym wątku lub może działać w tym samym wątku co Twój. Nie znam żadnego języka, który wspierałby to, co Twoim zdaniem Go powinno wspierać.
Jeremy Wall
5
@jeremy Nie zgadzam się z Go, ale Erlang pozwala zabić proces, który wykonuje funkcję pętli.
MatthewToday
10
Wielozadaniowość Go to współpraca, a nie zapobieganie. Goroutine w pętli nigdy nie wchodzi do programu planującego, więc nigdy nie można go zabić.
Jeff Allen
34
Nie możesz zabić gorutyny z zewnątrz. Możesz zasygnalizować goroutine, aby przestał używać kanału, ale nie ma uchwytu na goroutines, który mógłby wykonać jakiekolwiek zarządzanie meta. Goroutyny mają na celu wspólne rozwiązywanie problemów, więc zabicie kogoś, kto źle się zachowuje, prawie nigdy nie byłoby odpowiednią reakcją. Jeśli chcesz izolacji w celu zapewnienia niezawodności, prawdopodobnie potrzebujesz procesu.
I możesz zajrzeć do pakietu encoding / gob, który pozwoliłby dwóm programom Go łatwo wymieniać struktury danych przez potok.
Jeff Allen
W moim przypadku mam gorutynę, która zostanie zablokowana podczas wywołania systemowego i muszę jej powiedzieć, aby przerwała wywołanie systemowe, a następnie zakończyła. Gdybym został zablokowany na kanale odczytu, dałoby się zrobić to, co sugerujesz.
Omnifarious
Widziałem ten problem wcześniej. Sposób, w jaki "rozwiązaliśmy" to zwiększenie liczby wątków na początku aplikacji, aby dopasować liczbę goroutines, które mogłyby być + liczba procesorów
rouzier
20
Ogólnie rzecz biorąc, można utworzyć kanał i odebrać sygnał stopu w gorutynę.
W tym przykładzie istnieją dwa sposoby tworzenia kanału.
kanał
kontekst . W przykładzie pokażęcontext.WithCancel
Pierwsze demo, użyj channel:
package mainimport"fmt"import"time"
func do_stuff()int{return1}
func main(){
ch := make(chan int,100)done:= make(chan struct{})
go func(){for{select{case ch <- do_stuff():case<-done:
close(ch)return}
time.Sleep(100* time.Millisecond)}}()
go func(){
time.Sleep(3* time.Second)done<-struct{}{}}()for i := range ch {
fmt.Println("receive value: ", i)}
fmt.Println("finish")}
Drugie demo, użyj context:
package mainimport("context""fmt""time")
func main(){
forever := make(chan struct{})
ctx, cancel := context.WithCancel(context.Background())
go func(ctx context.Context){for{select{case<-ctx.Done():// if cancel() execute
forever <-struct{}{}returndefault:
fmt.Println("for loop")}
time.Sleep(500* time.Millisecond)}}(ctx)
go func(){
time.Sleep(3* time.Second)
cancel()}()<-forever
fmt.Println("finish")}
Wiem, że ta odpowiedź została już zaakceptowana, ale pomyślałem, że wrzucę do niej swoje 2 centy. Lubię korzystać z pakietu grobowca . Jest to w zasadzie superwizowany kanał wyjścia, ale robi też fajne rzeczy, takie jak przekazywanie wszelkich błędów. Kontrolowana procedura nadal odpowiada za sprawdzanie sygnałów zdalnego zabijania. Afaik, nie jest możliwe uzyskanie „identyfikatora” gorutyny i zabicie go, jeśli źle się zachowuje (np. Utknął w nieskończonej pętli).
Oto prosty przykład, który przetestowałem:
package mainimport("launchpad.net/tomb""time""fmt")
type Procstruct{Tomb tomb.Tomb}
func (proc *Proc)Exec(){
defer proc.Tomb.Done()// Must call only oncefor{select{case<-proc.Tomb.Dying():returndefault:
time.Sleep(300* time.Millisecond)
fmt.Println("Loop the loop")}}}
func main(){
proc :=&Proc{}
go proc.Exec()
time.Sleep(1* time.Second)
proc.Tomb.Kill(fmt.Errorf("Death from above"))
err := proc.Tomb.Wait()// Will return the error that killed the proc
fmt.Println(err)}
Wynik powinien wyglądać następująco:
# Loop the loop# Loop the loop# Loop the loop# Loop the loop# Death from above
Ten pakiet jest całkiem interesujący! Czy przetestowałeś, co tombrobi z gorutyną na wypadek, gdyby coś się w niej wydarzyło, na przykład wywołując panikę? Technicznie rzecz biorąc, wyjść goroutine w tym przypadku, więc jestem zakładając, że będzie nadal nazywają odroczony proc.Tomb.Done()...
Gwyneth Llewelyn
1
Cześć Gwyneth, tak proc.Tomb.Done(), uruchomiłoby się, zanim panika zawiesi program, ale po co? Jest możliwe, że główna gorutyna może mieć bardzo małe okno na wykonanie niektórych instrukcji, ale nie ma sposobu na wyjście z paniki w innej gorutyce, więc program nadal się zawiesza. Dokumenty mówią: „Kiedy funkcja F wywołuje panikę, wykonywanie F zatrzymuje się, wszelkie odroczone funkcje w F są wykonywane normalnie, a następnie F wraca do swojego wywołującego… Proces kontynuuje na stosie, aż wszystkie funkcje w bieżącym gorutynie powrócą, w tym momencie program ulega awarii. ”
Kevin Cantwell,
7
Osobiście chciałbym użyć zasięgu na kanale w goroutine:
Odpowiedzi:
EDYCJA: Napisałem tę odpowiedź w pośpiechu, zanim zdałem sobie sprawę, że twoje pytanie dotyczy wysyłania wartości do chan wewnątrz goroutine. Poniższe podejście można zastosować z dodatkowym kanałem, jak zasugerowano powyżej, lub korzystając z faktu, że kanał, który już masz, jest dwukierunkowy, możesz użyć tylko jednego ...
Jeśli twój goroutine istnieje wyłącznie po to, aby przetwarzać elementy wychodzące z kanału, możesz skorzystać z wbudowanego „zamknij” i specjalnego formularza odbioru dla kanałów.
Oznacza to, że kiedy skończysz wysyłać elementy na kanale, zamykasz go. Następnie wewnątrz swojego goroutine otrzymujesz dodatkowy parametr do operatora odbioru, który pokazuje, czy kanał został zamknięty.
Oto pełny przykład (grupa waitgroup służy do upewnienia się, że proces będzie kontynuowany do zakończenia gorutyny):
źródło
defer
wywołaniawg.Done()
irange ch
pętli do iteracji po wszystkich wartościach, aż kanał zostanie zamknięty.Zwykle podaje się goroutine (prawdopodobnie oddzielny) kanał sygnałowy. Ten kanał sygnałowy jest używany do wpychania wartości, kiedy chcesz, aby goroutine się zatrzymał. Sondaże goroutine, które regularnie przekazują kanał. Gdy tylko wykryje sygnał, kończy pracę.
źródło
Nie możesz zabić gorutyny z zewnątrz. Możesz zasygnalizować goroutine, aby przestał używać kanału, ale nie ma uchwytu na goroutines, który mógłby wykonać jakiekolwiek zarządzanie meta. Goroutyny mają na celu wspólne rozwiązywanie problemów, więc zabicie kogoś, kto źle się zachowuje, prawie nigdy nie byłoby odpowiednią reakcją. Jeśli chcesz izolacji w celu zapewnienia niezawodności, prawdopodobnie potrzebujesz procesu.
źródło
Ogólnie rzecz biorąc, można utworzyć kanał i odebrać sygnał stopu w gorutynę.
W tym przykładzie istnieją dwa sposoby tworzenia kanału.
kanał
kontekst . W przykładzie pokażę
context.WithCancel
Pierwsze demo, użyj
channel
:Drugie demo, użyj
context
:źródło
Wiem, że ta odpowiedź została już zaakceptowana, ale pomyślałem, że wrzucę do niej swoje 2 centy. Lubię korzystać z pakietu grobowca . Jest to w zasadzie superwizowany kanał wyjścia, ale robi też fajne rzeczy, takie jak przekazywanie wszelkich błędów. Kontrolowana procedura nadal odpowiada za sprawdzanie sygnałów zdalnego zabijania. Afaik, nie jest możliwe uzyskanie „identyfikatora” gorutyny i zabicie go, jeśli źle się zachowuje (np. Utknął w nieskończonej pętli).
Oto prosty przykład, który przetestowałem:
Wynik powinien wyglądać następująco:
źródło
tomb
robi z gorutyną na wypadek, gdyby coś się w niej wydarzyło, na przykład wywołując panikę? Technicznie rzecz biorąc, wyjść goroutine w tym przypadku, więc jestem zakładając, że będzie nadal nazywają odroczonyproc.Tomb.Done()
...proc.Tomb.Done()
, uruchomiłoby się, zanim panika zawiesi program, ale po co? Jest możliwe, że główna gorutyna może mieć bardzo małe okno na wykonanie niektórych instrukcji, ale nie ma sposobu na wyjście z paniki w innej gorutyce, więc program nadal się zawiesza. Dokumenty mówią: „Kiedy funkcja F wywołuje panikę, wykonywanie F zatrzymuje się, wszelkie odroczone funkcje w F są wykonywane normalnie, a następnie F wraca do swojego wywołującego… Proces kontynuuje na stosie, aż wszystkie funkcje w bieżącym gorutynie powrócą, w tym momencie program ulega awarii. ”Osobiście chciałbym użyć zasięgu na kanale w goroutine:
https://play.golang.org/p/qt48vvDu8cd
Dave napisał świetny post na ten temat: http://dave.cheney.net/2013/04/30/curious-channels .
źródło