Це хороший приклад режиму працівників і контролера в Go, написаний @Jimt, у відповідь на " Чи є якийсь елегантний спосіб призупинити та відновити будь-яку іншу програму в 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])
}
}
Але у цього коду також є проблема: якщо ви хочете видалити робочий канал workers
при worker()
виході, трапляється блокування.
Якщо ви close(workers[i])
, наступного разу, коли контролер запише в нього, це спричинить паніку, оскільки go не може писати на закритий канал. Якщо ви використовуєте якийсь мьютекс для його захисту, тоді він застрягне, workers[i] <- Running
оскільки worker
не читає нічого з каналу, а запис буде заблоковано, а мьютекс спричинить глухий замок. Ви також можете надати більший буфер для каналу як робочого процесу, але це недостатньо добре.
Тому я думаю, що найкращим способом вирішити цю проблему є worker()
закритий канал при виході, якщо контролер виявить канал закритим, він перестрибне через нього і нічого не зробить. Але я не можу знайти, як перевірити, чи канал уже закритий чи ні, у цій ситуації. Якщо я спробую прочитати канал у контролері, контролер може бути заблокований. Тому я зараз дуже розгублений.
PS: Відновлення піднятої паніки - це те, що я намагався, але це закриє горутину, яка підняла паніку. У цьому випадку це буде контролер, тому користі немає.
Тим не менше, я думаю, що команді Go корисно реалізувати цю функцію в наступній версії Go.