Voici un bon exemple de mode workers & controller en Go écrit par @Jimt, en réponse à la question suivante " Existe-t-il un moyen élégant de mettre en pause et de reprendre n'importe quelle autre goroutine dans 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])
}
}
Mais ce code a aussi un problème : Si vous voulez supprimer un canal de travailleur dans workers
cuando worker()
sort, un verrou mort se produit.
Si usted close(workers[i])
La prochaine fois qu'un contrôleur y écrit, cela provoque une panique car il ne peut pas écrire dans un canal fermé. Si vous utilisez un mutex pour le protéger, alors il sera bloqué sur workers[i] <- Running
depuis le worker
ne lit rien depuis le canal et l'écriture sera bloquée, et le mutex causera un verrou mort. Vous pouvez aussi donner un plus grand tampon au canal comme solution de contournement, mais ce n'est pas suffisant.
Donc je pense que la meilleure façon de résoudre ce problème est de worker()
fermer le canal à la sortie, si le contrôleur trouve un canal fermé, il sautera par-dessus et ne fera rien. Mais je ne trouve pas comment vérifier si un canal est déjà fermé ou non dans cette situation. Si j'essaie de lire le canal dans le contrôleur, le contrôleur pourrait être bloqué. Je suis donc très confus pour le moment.
PS : J'ai essayé de récupérer la panique soulevée, mais cela fermera la goroutine qui a soulevé la panique. Dans ce cas, il s'agira d'un contrôleur, donc ce n'est pas utile.
Néanmoins, je pense qu'il est utile pour l'équipe de Go d'implémenter cette fonction dans la prochaine version de Go.