当前位置: 首页 > 面试题库 >

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

尚嘉庆
2023-03-14
问题内容

这是@Jimt在Go中编写的工作程序和控制器模式的一个很好的示例,

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()发生死锁。

如果您使用close(workers[i]),下次控制器写入该命令将导致恐慌,因为go无法写入一个已关闭的通道。如果您使用某些互斥锁来保护它,则workers[i] <- Running由于worker它不会从通道读取任何内容,并且将阻止写入,因此互斥锁将导致死锁。您还可以为频道提供更大的缓冲,以解决此问题,但这还不够。

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

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

不过,我认为Go团队在下一个Go版本中实现此功能还是有用的。


问题答案:

通过变通的方式,可以通过恢复引发的恐慌来尝试写入的通道。但是,如果不从中读取一个读取通道,则无法检查它是否已关闭。

要么你会

  • 最终从中读取“ true”值(v <- c
  • 读取“真”值和“未关闭”指示符(v, ok <- c
  • 读取零值和“已关闭”指示器(v, ok <- c
  • 会永远封锁在频道中读取(v <- c

从技术上讲,只有最后一个不会从该频道读取,但这没什么用。



 类似资料:
  • 问题内容: 我想知道an 是否为空,但是没有使用方法。有没有办法知道它是否为空,而无需从中读取? 问题答案: 我想你在找。它不会告诉您它是否为空,但可以指示您是否要读取数据。

  • 问题内容: 我正在尝试编写一个脚本,该脚本需要根据CSS选择器找到的特定浏览器对象是否存在来适应其工作流程行为。 我不想使用document.getElementByID方法,因为从技术上讲,这不是CSS选择器,并且我们整个企业都在CSS选择器上进行了标准化,因此在DOM以外的其他任何选择都不会使CSS选择器超出我们的代码审查过程无论如何。 ThingSelector在哪里: OpenThing在

  • 问题内容: 有没有一种方法可以确定给定的示例实体类: 所谓“新”,是指有人调用了新的A()甚至可能设置了名称,但从未保存或保留。 通常,人们可以检查id,但是我想要一种不需要id或getId()方法的解决方案。 基本上,即使在分离模式下,此实体也已持久化到数据库中。 @Version或getVersion也不是令人满意的解决方案。 也许是独立的|| isAttached可能有效,但是我不确定如何在

  • 这个问题用泛型约束标记为重复,where T:struct和where T:class和旧标题,它是。所以我更新了标题,以便更好地表达我的问题。我想问的是,如何调用泛型方法并仅在参数不为null的情况下才使用该参数,而不使用装箱。 一些为链接问题解释的变通方法可以用来回答这个问题,但我仍然相信这是一个根本不同的问题。

  • 我遇到了这个PHP代码使用SMTP检查电子邮件地址而不发送电子邮件。 有没有人尝试过类似的方法,或者对你有用吗?你能判断客户/用户输入的电子邮件是否正确和是否存在吗?

  • 问题内容: 有没有办法在不使用机制的情况下判断字符串是否表示整数(例如但不是或)? 问题答案: 如果你真的很讨厌在各处使用s,请编写一个辅助函数: 它将需要更多的代码来完全覆盖Python认为是整数的所有字符串。我说这是pythonic。