Dies ist ein gutes Beispiel für den Worker & Controller-Modus in Go, geschrieben von @Jimt, als Antwort auf " Gibt es eine elegante Möglichkeit, eine andere Goroutine in Golang anzuhalten und fortzusetzen? "
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])
}
}
Dieser Code hat jedoch auch ein Problem: Wenn Sie einen Worker-Kanal in entfernen möchten workers
, wenn worker()
Ausfahrten, Tot - Schloss geschieht.
Wenn Sie das close(workers[i])
nächste Mal in den Controller schreiben, wird eine Panik ausgelöst, da go nicht in einen geschlossenen Kanal schreiben kann. Wenn Sie zum Schutz einen Mutex verwenden, bleibt dieser haftenworkers[i] <- Running
da worker
der Kanal nichts liest und das Schreiben blockiert wird und der Mutex eine Deadlock-Funktion verursacht. Sie können dem Kanal auch einen größeren Puffer als Workaround geben, aber er ist nicht gut genug.
Ich denke, der beste Weg, dies zu lösen, ist worker()
Schließen des Kanals beim Beenden. Wenn der Controller einen geschlossenen Kanal findet, springt er darüber und tut nichts. Ich kann jedoch nicht feststellen, wie überprüft werden kann, ob ein Kanal in dieser Situation bereits geschlossen ist oder nicht. Wenn ich versuche, den Kanal im Controller zu lesen, ist der Controller möglicherweise blockiert. Ich bin also vorerst sehr verwirrt.
PS: Ich habe versucht, die erhöhte Panik wiederherzustellen, aber sie wird die Goroutine schließen, die die Panik ausgelöst hat. In diesem Fall handelt es sich um einen Controller, der keinen Nutzen hat.
Dennoch denke ich, dass es für das Go-Team nützlich ist, diese Funktion in der nächsten Version von Go zu implementieren.