Eu tenho uma goroutine que chama um método e passa o valor retornado em um canal:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Como faço para impedir tal goroutine?
Eu tenho uma goroutine que chama um método e passa o valor retornado em um canal:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Como faço para impedir tal goroutine?
Respostas:
EDIT: Eu escrevi esta resposta às pressas, antes de perceber que sua pergunta é sobre como enviar valores para um chan dentro de uma goroutine. A abordagem abaixo pode ser usada com um canal adicional, conforme sugerido acima, ou usando o fato de que o canal que você já possui é bidirecional, você pode usar apenas um ...
Se sua goroutine existe apenas para processar os itens que saem do canal, você pode usar o "close" embutido e o formulário especial de recepção para canais.
Ou seja, quando terminar de enviar itens ao canal, você o fecha. Então, dentro de sua goroutine, você obtém um parâmetro extra para o operador de recepção que mostra se o canal foi fechado.
Aqui está um exemplo completo (o grupo de espera é usado para garantir que o processo continue até que a goroutine seja concluída):
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()
e um range ch
loop para iterar sobre todos os valores até que o canal seja fechado.
Normalmente, você passa pela goroutine um canal de sinal (possivelmente separado). Esse canal de sinal é usado para enviar um valor para quando você quiser que o goroutine pare. A goroutine faz pesquisas nesse canal regularmente. Assim que detecta um sinal, ele fecha.
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
Você não pode matar um goroutine de fora. Você pode sinalizar a uma goroutine para parar de usar um canal, mas não há controle sobre as goroutines para fazer qualquer tipo de meta-gerenciamento. Goroutines se destinam a resolver problemas de forma cooperativa, portanto, matar alguém que esteja se comportando mal quase nunca seria uma resposta adequada. Se você deseja isolamento para obter robustez, provavelmente deseja um processo.
Geralmente, você pode criar um canal e receber um sinal de parada no goroutine.
Há duas maneiras de criar um canal neste exemplo.
canal
contexto . No exemplo vou demonstrarcontext.WithCancel
A primeira demonstração, use 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")
}
A segunda demonstração, use 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")
}
Sei que essa resposta já foi aceita, mas pensei em jogar meus 2 centavos. Gosto de usar o pacote de tumba . É basicamente um canal de saída atualizado, mas também faz coisas boas, como devolver quaisquer erros. A rotina sob controle ainda tem a responsabilidade de verificar os sinais de eliminação remota. Afaik, não é possível obter um "id" de um goroutine e matá-lo se ele estiver se comportando mal (ou seja: preso em um loop infinito).
Aqui está um exemplo simples que testei:
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)
}
A saída deve ser semelhante a:
# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above
tomb
faz com a goroutine caso aconteça alguma coisa dentro dela que cause pânico, por exemplo? Tecnicamente falando, o goroutine sai neste caso, então presumo que ele ainda chamará o adiado proc.Tomb.Done()
...
proc.Tomb.Done()
seria executado antes que o pânico travasse o programa, mas para quê? É possível que a goroutine principal tenha uma janela de oportunidade muito pequena para executar algumas instruções, mas não há como se recuperar de um pânico em outra goroutine, então o programa ainda trava. Os documentos dizem: "Quando a função F chama o pânico, a execução de F é interrompida, todas as funções adiadas em F são executadas normalmente e, em seguida, F retorna para seu chamador .. O processo continua na pilha até que todas as funções na goroutine atual tenham retornado, ponto em que o programa trava. "
Pessoalmente, gostaria de usar intervalo em um canal em um goroutine:
https://play.golang.org/p/qt48vvDu8cd
Dave escreveu um ótimo post sobre isso: http://dave.cheney.net/2013/04/30/curious-channels .