本文介绍了如何在不阅读的情况下检查通道是否关闭?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是工人和工人的一个很好的例子。 @Jimt编写的Go语言中的控制器模式,以回答

This is a good example of workers & controller mode in Go written by @Jimt, in answer to"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])
    }
}

但是此代码也有一个问题:如果要删除 workers中的工作频道, worker()退出时,会发生死锁。

But this code also has an issue: If you want to remove a worker channel in workers when worker() exits, dead lock happens.

如果您 close(workers [i]),下一次控制器写入它会引起恐慌,因为go无法写入一个关闭的通道。如果您使用一些互斥锁来保护它,则它将被卡在 worker [i]上-自 worker c>没有从通道读取任何内容,写入将被阻止,互斥锁将导致无效锁定。您也可以通过更大的缓冲来解决问题,但这还不够。

If you close(workers[i]), next time controller writes into it will cause a panic since go can't write into a closed channel. If you use some mutex to protect it, then it will be stuck on workers[i] <- Running since the worker is not reading anything from the channel and write will be blocked, and mutex will cause a dead lock. You can also give a bigger buffer to channel as a work-around, but it's not good enough.

因此,我认为解决此问题的最佳方法是 worker()退出时关闭通道,如果控制器发现关闭的通道,它将跳过该通道并且不执行任何操作。但是在这种情况下,我找不到如何检查频道是否已关闭的信息。如果尝试读取控制器中的通道,则控制器可能被阻止。因此,我现在很困惑。

So I think the best way to solve this is worker() close channel when exits, if the controller finds a channel closed, it will jump over it and do nothing. But I can't find how to check a channel is already closed or not in this situation. If I try to read the channel in controller, the controller might be blocked. So I'm very confused for now.

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

PS: Recovering the raised panic is what I have tried, but it will close goroutine which raised panic. In this case it will be controller so it's no use.

我仍然认为Go团队在下一版Go中实现此功能很有用。

Still, I think it's useful for Go team to implement this function in next version of Go.

推荐答案

可以通过变通的方式,通过恢复引发的恐慌来尝试写入的通道。但是您无法检查是否已关闭读取通道而不关闭读取通道。

In a hacky way it can be done for channels which one attempts to write to by recovering the raised panic. But you cannot check if a read channel is closed without reading from it.

您要么会


  • 最终从中读取 true值( v<-c

  • 读取 true值值和未关闭指示符( v,确定<-c

  • 读取零值和已关闭指示符( v,ok<-c

  • 将永远阻塞读取的频道( v< -c

  • eventually read the "true" value from it (v <- c)
  • read the "true" value and 'not closed' indicator (v, ok <- c)
  • read a zero value and the 'closed' indicator (v, ok <- c)
  • will block in the channel read forever (v <- c)

从技术上讲,只有最后一个不会从频道中读取,但这很少使用。

Only the last one technically doesn't read from the channel, but that's of little use.

这篇关于如何在不阅读的情况下检查通道是否关闭?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-26 04:57
查看更多