Mam goroutine, który wywołuje metodę i przekazuje zwróconą wartość na kanale:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Jak zatrzymać taką gorutynę?
Mam goroutine, który wywołuje metodę i przekazuje zwróconą wartość na kanale:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Jak zatrzymać taką gorutynę?
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):
package main
import "sync"
func main() {
var wg sync.WaitGroup
wg.Add(1)
ch := make(chan int)
go func() {
for {
foo, ok := <- ch
if !ok {
println("done")
wg.Done()
return
}
println(foo)
}
}()
ch <- 1
ch <- 2
ch <- 3
close(ch)
wg.Wait()
}
defer
wywołania wg.Done()
i range 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ę.
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
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.
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 main
import "fmt"
import "time"
func do_stuff() int {
return 1
}
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 main
import (
"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{}{}
return
default:
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 main
import (
"launchpad.net/tomb"
"time"
"fmt"
)
type Proc struct {
Tomb tomb.Tomb
}
func (proc *Proc) Exec() {
defer proc.Tomb.Done() // Must call only once
for {
select {
case <-proc.Tomb.Dying():
return
default:
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
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ą odroczony proc.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 .