Este es un buen ejemplo del modo de trabajadores y controlador en Go escrito por @Jimt, en respuesta a " ¿Hay alguna forma elegante de pausar y reanudar cualquier otra rutina de gor en golang? "
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
// Possible worker states.
const (
Stopped = 0
Paused = 1
Running = 2
)
// Maximum number of workers.
const WorkerCount = 1000
func main() {
// Launch workers.
var wg sync.WaitGroup
wg.Add(WorkerCount + 1)
workers := make([]chan int, WorkerCount)
for i := range workers {
workers[i] = make(chan int)
go func(i int) {
worker(i, workers[i])
wg.Done()
}(i)
}
// Launch controller routine.
go func() {
controller(workers)
wg.Done()
}()
// Wait for all goroutines to finish.
wg.Wait()
}
func worker(id int, ws <-chan int) {
state := Paused // Begin in the paused state.
for {
select {
case state = <-ws:
switch state {
case Stopped:
fmt.Printf("Worker %d: Stopped\n", id)
return
case Running:
fmt.Printf("Worker %d: Running\n", id)
case Paused:
fmt.Printf("Worker %d: Paused\n", id)
}
default:
// We use runtime.Gosched() to prevent a deadlock in this case.
// It will not be needed of work is performed here which yields
// to the scheduler.
runtime.Gosched()
if state == Paused {
break
}
// Do actual work here.
}
}
}
// controller handles the current state of all workers. They can be
// instructed to be either running, paused or stopped entirely.
func controller(workers []chan int) {
// Start workers
for i := range workers {
workers[i] <- Running
}
// Pause workers.
<-time.After(1e9)
for i := range workers {
workers[i] <- Paused
}
// Unpause workers.
<-time.After(1e9)
for i := range workers {
workers[i] <- Running
}
// Shutdown workers.
<-time.After(1e9)
for i := range workers {
close(workers[i])
}
}
Pero este código también tiene un problema: si desea eliminar un canal de trabajador workers
cuando worker()
sale, se produce un bloqueo.
Si usted close(workers[i])
, la próxima vez que el controlador escriba en él, se producirá un pánico, ya que go no puede escribir en un canal cerrado. Si usa algún mutex para protegerlo, se bloqueará workers[i] <- Running
ya worker
que no lee nada del canal y la escritura se bloqueará, y el mutex provocará un bloqueo muerto. También puede dar un búfer más grande al canal como solución temporal, pero no es lo suficientemente bueno.
Así que creo que la mejor manera de resolver esto es worker()
cerrar el canal cuando sale, si el controlador encuentra un canal cerrado, lo saltará y no hará nada. Pero no encuentro cómo comprobar que un canal ya está cerrado o no en esta situación. Si intento leer el canal en el controlador, es posible que el controlador esté bloqueado. Entonces estoy muy confundido por ahora.
PD: Recuperar el pánico levantado es lo que he intentado, pero cerrará la rutina que provocó el pánico. En este caso será el controlador, por lo que no sirve de nada.
Aún así, creo que es útil para el equipo de Go implementar esta función en la próxima versión de Go.