Pregunta

Tengo una goroutina que llama a un método y pasa el valor devuelto en un canal:

ch := make(chan int, 100)
go func(){
    for {
        ch <- do_stuff()
    }
}()

¿Cómo detengo una goroutina así?

¿Fue útil?

Solución

EDITAR: Escribí esta respuesta a toda prisa, antes de darme cuenta de que su pregunta se trata de enviar valores a un chan dentro de una goroutina. El siguiente enfoque se puede usar con un chan adicional como se sugirió anteriormente, o utilizando el hecho de que el chan que ya tiene es bidireccional, puede usar solo el ...

Si su Goroutine existe únicamente para procesar los elementos que salen del Chan, puede hacer uso de la construcción "cerrada" y el formulario de recepción especial para los canales.

Es decir, una vez que hayas terminado de enviar artículos al chan, lo cierras. Luego, dentro de su goroutine obtendrá un parámetro adicional al operador de recepción que muestra si el canal ha sido cerrado.

Aquí hay un ejemplo completo (el grupo de espera se usa para asegurarse de que el proceso continúe hasta que se complete la goroutina):

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()
}

Otros consejos

Por lo general, pasa el canal de señal Goroutine A (posiblemente separado). Ese canal de señal se usa para impulsar un valor cuando desea que la goroutina se detenga. Las encuestas de Goroutine que canalizan regularmente. Tan pronto como detecta una señal, se deja.

quit := make(chan bool)
go func() {
    for {
        select {
        case <- quit:
            return
        default:
            // Do other stuff
        }
    }
}()

// Do stuff

// Quit goroutine
quit <- true

No puedes matar una goroutina desde afuera. Puede indicar que una goroutina deje de usar un canal, pero no hay manejo de Goroutines para hacer ningún tipo de meta gestión. Las goroutinas están destinadas a resolver los problemas de manera cooperativa, por lo que matar uno que se porta mal casi nunca sería una respuesta adecuada. Si desea un aislamiento para la robustez, probablemente desee un proceso.

En general, puede crear un canal y recibir una señal de parada en la Goroutine.

Hay dos formas de crear canal en este ejemplo.

  1. canal

  2. contexto. En el ejemplo lo demo context.WithCancel

La primera demostración, usar 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")
}

La segunda demostración, usar 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")
}

Sé que esta respuesta ya ha sido aceptada, pero pensé en tirar mis 2cents. Me gusta usar el tumba paquete. Básicamente es un canal de dejar de lado, pero también hace cosas buenas como dejar cualquier error. La rutina bajo control todavía tiene la responsabilidad de verificar las señales de muerte remotas. AFAIK No es posible obtener una "identificación" de una goroutina y matarla si se porta mal (es decir: atascado en un bucle infinito).

Aquí hay un ejemplo simple que probé:

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)
}

La salida debe verse como:

# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above

Personalmente, me gustaría usar el rango en un canal en una goroutina:

https://play.golang.org/p/qt48vvdu8cd

Dave ha escrito una gran publicación sobre esto: http://dave.cheney.net/2013/04/30/curious-channels.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top