У меня есть горутина, которая вызывает метод и передает возвращаемое значение по каналу:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Как мне остановить такую горутину?
У меня есть горутина, которая вызывает метод и передает возвращаемое значение по каналу:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Как мне остановить такую горутину?
Ответы:
РЕДАКТИРОВАТЬ: Я написал этот ответ в спешке, прежде чем понял, что ваш вопрос касается отправки значений чану внутри горутины. Приведенный ниже подход можно использовать либо с дополнительным каналом, как предложено выше, либо с учетом того факта, что канал, который у вас уже есть, является двунаправленным, вы можете использовать только один ...
Если ваша горутина существует исключительно для обработки элементов, выходящих из чана, вы можете использовать встроенную функцию «закрыть» и специальную форму приема для каналов.
То есть, как только вы закончите отправлять элементы на чан, вы закрываете его. Затем внутри вашей горутины вы получаете дополнительный параметр для оператора приема, который показывает, был ли канал закрыт.
Вот полный пример (группа ожидания используется, чтобы убедиться, что процесс продолжается до завершения горутины):
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
to call wg.Done()
и range ch
цикла для перебора всех значений, пока канал не будет закрыт.
Обычно вы передаете горутине (возможно, отдельный) сигнальный канал. Этот сигнальный канал используется для ввода значения, когда вы хотите, чтобы горутина остановилась. Горутина регулярно опрашивает этот канал. Как только он обнаруживает сигнал, он закрывается.
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
Вы не можете убить горутину извне. Вы можете сигнализировать горутине о прекращении использования канала, но у горутины нет ручки для какого-либо управления метаданными. Горутины предназначены для совместного решения проблем, поэтому убийство плохо себя ведет почти никогда не будет адекватным ответом. Если вам нужна изоляция для устойчивости, вам, вероятно, нужен процесс.
Как правило, вы можете создать канал и получить стоп-сигнал в горутине.
В этом примере есть два способа создать канал.
канал
контекст . В примере я продемонстрируюcontext.WithCancel
Первая демонстрация, используйте 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")
}
Вторая демонстрация, используйте 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")
}
Я знаю, что этот ответ уже принят, но я подумал, что брошу свои 2 цента. Мне нравится использовать гробницу пакет . По сути, это усовершенствованный канал выхода, но он также делает приятные вещи, например, возвращает любые ошибки. Подпрограмма, находящаяся под контролем, по-прежнему отвечает за проверку сигналов удаленного отключения. Afaik невозможно получить "id" горутины и убить ее, если она плохо себя ведет (то есть: застряла в бесконечном цикле).
Вот простой пример, который я тестировал:
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)
}
Результат должен выглядеть так:
# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above
tomb
делает горутина, если внутри нее что-то происходит, например, вызывая панику? Технически говоря, в этом случае горутина завершается, поэтому я предполагаю, что она все равно будет вызывать отложенный proc.Tomb.Done()
...
proc.Tomb.Done()
будет выполнено до того, как паника приведет к сбою программы, но с какой целью? Возможно, что у основной горутины может быть очень небольшое окно возможностей для выполнения некоторых операторов, но у нее нет способа восстановиться после паники в другой горутине, поэтому программа все равно вылетает. Документы говорят: «Когда функция F вызывает панику, выполнение F останавливается, любые отложенные функции в F выполняются нормально, а затем F возвращается к вызывающему. Процесс продолжается вверх по стеку, пока не вернутся все функции в текущей горутине, в этот момент программа вылетает ".
Лично я хотел бы использовать диапазон на канале в горутине:
https://play.golang.org/p/qt48vvDu8cd
Дэйв написал об этом отличный пост: http://dave.cheney.net/2013/04/30/curious-channels .