猿问

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

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.

        }

    }

}



但是这段代码也有一个问题:如果你想在退出workers时删除一个工作通道,worker()就会发生死锁。


如果你close(workers[i]),下次控制器写入它会导致恐慌,因为 go 无法写入关闭的通道。如果你使用一些互斥锁来保护它,那么它会被卡住,workers[i] <- Running因为worker它没有从通道读取任何东西,写操作会被阻塞,互斥锁会导致死锁。作为变通方法,您还可以为通道提供更大的缓冲区,但这还不够好。


所以我认为解决这个问题的最好方法是worker()退出时关闭通道,如果控制器发现通道关闭,它会跳过它并且什么也不做。但是在这种情况下,我找不到如何检查通道是否已关闭。如果我尝试读取控制器中的通道,则控制器可能被阻塞。所以我现在很困惑。


PS:恢复引发的恐慌是我尝试过的,但它会关闭引发恐慌的 goroutine。在这种情况下,它将是控制器,所以它没有用。


尽管如此,我认为 Go 团队在下一个版本的 Go 中实现这个功能是有用的。


哆啦的时光机
浏览 196回答 3
3回答

ABOUTYOU

以一种骇人听闻的方式,可以通过恢复引发的恐慌来为试图写入的通道完成。但是您无法在不读取的情况下检查读取通道是否已关闭。要么你会最终从中读取“真”值 (&nbsp;v <- c)读取“真”值和“未关闭”指标 (&nbsp;v, ok <- c)读取零值和“关闭”指示器 (&nbsp;v, ok <- c)将永远阻塞在通道中读取 (&nbsp;v <- c)只有最后一个在技术上不会从通道中读取,但这没什么用。

三国纷争

没有办法编写一个安全的应用程序,您需要知道一个通道是否打开而不与它交互。做你想做的事情的最好方法是使用两个渠道——一个用于工作,一个用于表示改变状态的愿望(以及完成状态改变,如果这很重要)。渠道便宜。复杂的设计重载语义不是。[还]<-time.After(1e9)是一种非常令人困惑且不明显的写作方式time.Sleep(time.Second)保持简单,每个人(包括你)都能理解它们。
随时随地看视频慕课网APP

相关分类

Go
我要回答