这是@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中实现这个功能是有用的.

推荐答案

以一种骇人的方式,它可以通过恢复引起的panic 来为试图写信的频道完成.但是,如果没有读取,则无法判断读取通道是否已关闭.

不是你愿意,就是你愿意

  • 最终从其中读取"true"值(v <- c)
  • 读取"TRUE"值和"未关闭"指示器(v, ok <- c)
  • 读取zero value和"关闭"指示器(v, ok <- c)(example)
  • 频道里的挡路会不会永远读(百)

从技术上讲,只有最后一个不能从通道中读取,但这没有什么用处.

Go相关问答推荐

如何在Deliverq中高效地传达客户依赖关系?

如果添加构建标签,gopls将停止工作

Golang应用程序:所请求的资源上不存在HTTP-Control-Allow-Origin标头

如何将GoFr筛选器用于查询参数?

无法在32位计算机上运行Golang应用程序

如何使redis池的等待超时

如何模拟go的Elastic search SDK?

go-chi: 接受带有反斜杠的 url 路径参数

Golang prometheus:有没有办法衡量出站请求的指标?

从给定顶点查找图形中所有闭合路径的算法

对 CSV 进行单元测试失败

读取非UTF8编码的文件内容并正确打印出来

如何使用 Docker 引擎 SDK 和 Golang 运行 docker 挂载卷

如何仅提取时间作为持续时间

来自洪流公告的奇怪同行字段

分配空切片而不引用其类型?

无限期运行 Go routine (完成后重新启动)

如何在 Windows 中使用 github.com/AllenDang/giu 和 github.com/gordonklaus/portaudio 构建 GO 程序

如何从应用程序调用谷歌云身份 API

Gin中测试模式有什么用