这是@Jimt在Go中编写的工作程序和控制器模式的一个很好的示例,以回答“在golang中是否有某种优雅的方式来暂停和恢复任何其他goroutine? ”
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:恢复引发恐慌是我尝试过的方法,但是它将关闭引起恐慌的goroutine。在这种情况下它将是控制器,所以没有用。
尽管如此,我认为对于Go团队在下一版Go中实现此功能还是有用的。