package mainimport ( "fmt" "runtime" "sync" "time")// Possible worker states.const ( Stopped = 0 Paused = 1 Running = 2)// Maximum number of workers.const WorkerCount = 1000func 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. } }}但是这段代码也有一个问题:如果你想在退出workers时删除一个工作通道,worker()就会发生死锁。如果你close(workers[i]),下次控制器写入它会导致恐慌,因为 go 无法写入关闭的通道。如果你使用一些互斥锁来保护它,那么它会被卡住,workers[i] <- Running因为worker它没有从通道读取任何东西,写操作会被阻塞,互斥锁会导致死锁。作为变通方法,您还可以为通道提供更大的缓冲区,但这还不够好。所以我认为解决这个问题的最好方法是worker()退出时关闭通道,如果控制器发现通道关闭,它会跳过它并且什么也不做。但是在这种情况下,我找不到如何检查通道是否已关闭。如果我尝试读取控制器中的通道,则控制器可能被阻塞。所以我现在很困惑。PS:恢复引发的恐慌是我尝试过的,但它会关闭引发恐慌的 goroutine。在这种情况下,它将是控制器,所以它没有用。尽管如此,我认为 Go 团队在下一个版本的 Go 中实现这个功能是有用的。
3 回答
ABOUTYOU
TA贡献1812条经验 获得超5个赞
以一种骇人听闻的方式,可以通过恢复引发的恐慌来为试图写入的通道完成。但是您无法在不读取的情况下检查读取通道是否已关闭。
要么你会
最终从中读取“真”值 (
v <- c
)读取“真”值和“未关闭”指标 (
v, ok <- c
)读取零值和“关闭”指示器 (
v, ok <- c
)将永远阻塞在通道中读取 (
v <- c
)
只有最后一个在技术上不会从通道中读取,但这没什么用。
三国纷争
TA贡献1804条经验 获得超7个赞
没有办法编写一个安全的应用程序,您需要知道一个通道是否打开而不与它交互。
做你想做的事情的最好方法是使用两个渠道——一个用于工作,一个用于表示改变状态的愿望(以及完成状态改变,如果这很重要)。
渠道便宜。复杂的设计重载语义不是。
[还]
<-time.After(1e9)
是一种非常令人困惑且不明显的写作方式
time.Sleep(time.Second)
保持简单,每个人(包括你)都能理解它们。
- 3 回答
- 0 关注
- 195 浏览
添加回答
举报
0/150
提交
取消