这是@jimt编写的Go中的Worker&;Controller模式的一个很好的示例,回答是 "Is there some elegant way to pause & resume any other goroutine in 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])
}
}
但这段代码也有一个问题:如果您想在worker()
退出时删除workers
中的工作通道,就会发生死锁.
如果你是close(workers[i])
,下一次控制器写入时会导致死机,因为围棋不能写入关闭的通道.如果您使用一些互斥体来保护它,那么它将被卡在workers[i] <- Running
上,因为worker
没有从通道中读取任何内容,写入将被阻塞,并且互斥体将导致死锁.您也可以为通道提供更大的缓冲区作为解决办法,但这还不够好.
所以我认为解决这个问题的最好方法是退出时关闭worker()
个通道,如果控制器发现一个通道关闭,它会跳过它,什么也不做.但在这种情况下,我找不到如何判断频道是否已经关闭.如果我试图读取控制器中的通道,控制器可能会被阻塞.所以我现在很困惑.
附言:恢复引起的panic 是我已经try 过的,但是它将关闭引起panic 的大猩猩.在这种情况下,它将是控制器,所以它没有任何用处.
不过,我认为Go团队在下一个版本的Go中实现这个功能是有用的.