为了账号安全,请及时绑定邮箱和手机立即绑定

如何在不阅读的情况下检查通道是否关闭?

如何在不阅读的情况下检查通道是否关闭?

Go
哆啦的时光机 2021-06-04 07:05:08
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)

只有最后一个在技术上不会从通道中读取,但这没什么用。


查看完整回答
反对 回复 2021-06-07
?
三国纷争

TA贡献1804条经验 获得超7个赞

没有办法编写一个安全的应用程序,您需要知道一个通道是否打开而不与它交互。

做你想做的事情的最好方法是使用两个渠道——一个用于工作,一个用于表示改变状态的愿望(以及完成状态改变,如果这很重要)。

渠道便宜。复杂的设计重载语义不是。

[还]

<-time.After(1e9)

是一种非常令人困惑且不明显的写作方式

time.Sleep(time.Second)

保持简单,每个人(包括你)都能理解它们。


查看完整回答
反对 回复 2021-06-07
  • 3 回答
  • 0 关注
  • 195 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信