select - 了解使用和不使用 goroutine 从 channel 中选择

标签 select go closures channel goroutine

你好 stackoverflow 社区, 我正在使用 github.com/fsnotify/fsnotify将观察者设置为 Go 中的文件。我的功能看起来像

func SetWatcher(filename string) {
    fmt.Println("Setting watcher to file ", filename)
    Watcher, err = fsnotify.NewWatcher()
    if err != nil {
        fmt.Println("inotify errored. Other methods needs to be implemented.")
        panic(err)
    }
    if err != nil {
        log.Fatal(err)
    }

    done := make(chan bool)
    go func() {
        for {
            select {
            case event := <-Watcher.Events:
                if event.Op == fsnotify.Remove {
                    fmt.Println("File removed, needs to kill the process.")
                } else if event.Op == fsnotify.Rename {
                    fmt.Println("File renamed, need to restart seeking.")
                }

            case err := <-Watcher.Errors:
                log.Println("error:", err)
            }
        }
    }()

    err = Watcher.Add(filename)
    if err != nil {
        log.Fatal(err)
    }
    <-done
}

目前有效,我得到的输出为

Setting watcher to file  /var/log/syslog
File renamed, need to restart seeking.

但是,如果我尝试删除在 goroutine 中运行的闭包并将其运行为

func SetWatcher(filename string) {
    fmt.Println("Setting watcher to file ", filename)
    Watcher, err = fsnotify.NewWatcher()
    if err != nil {
        fmt.Println("inotify errored. Other methods needs to be implemented.")
        panic(err)
    }
    if err != nil {
        log.Fatal(err)
    }
    //defer Watcher.Close()

    //done := make(chan bool)
    //go func() {
    //  for {
            select {
            case event := <-Watcher.Events:
                if event.Op == fsnotify.Remove {
                    fmt.Println("File removed, needs to kill the process.")
                } else if event.Op == fsnotify.Rename {
                    fmt.Println("File renamed, need to restart seeking.")
                }

            case err := <-Watcher.Errors:
                log.Println("error:", err)
            }
        //}
    //}()

    err = Watcher.Add(filename)
    if err != nil {
        log.Fatal(err)
    }
    //<-done
}

程序从不输出任何东西。我用 strace 运行它,程序卡在了

[pid  5773] pselect6(0, NULL, NULL, NULL, {tv_sec=0, tv_nsec=20000}, NULL <unfinished ...>
[pid  5772] epoll_wait(4, [], 128, 0)   = 0
[pid  5772] futex(0x598bf8, FUTEX_WAIT, 0, NULL <unfinished ...>
[pid  5773] <... pselect6 resumed> )    = 0 (Timeout)
[pid  5740] <... pselect6 resumed> )    = 0 (Timeout)
[pid  5773] futex(0x598578, FUTEX_WAIT, 0, {tv_sec=60, tv_nsec=0} 
<unfinished ...>
[pid  5740] futex(0xae1f58, FUTEX_WAIT, 0, {tv_sec=60, tv_nsec=0}

并且没有从 channel 接收并继续等待。

我读到这可能是由于非缓冲 channel 引起的。因此,为了验证这一点,我修改了库以使用缓冲 channel ,并且生成新观察者的函数部分看起来像

    w := &Watcher{
    fd:       fd,
    poller:   poller,
    watches:  make(map[string]*watch),
    paths:    make(map[int]string),
    Events:   make(chan Event, 10), // Made it buffered here
    Errors:   make(chan error),
    done:     make(chan struct{}),
    doneResp: make(chan struct{}),
}

但行为是一样的。有人能帮我理解为什么在 goroutine 中从 channel 读取有效,而没有 goroutine 就不起作用吗?

谢谢。

最佳答案

我不熟悉 fsnotify,但看起来它不会在 channel 上发送任何东西,直到调用 Watcher.Add() 来监视文件,但是 Watcher. Add() 在第二个版本中发生在 select 之后。在这种情况下,select 将永远阻塞,因为未调用 Watcher.Add() 时无法发出任何信号。因此存在死锁。

关于select - 了解使用和不使用 goroutine 从 channel 中选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50079216/

相关文章:

MySQL - 如何从最接近开始的点获取范围?

php - 将类添加到 ACF 多选

mysql 通过多个条件选择行

python - 在 Python 中反射(reflect)/检查封闭变量

ios - viewDidLoad 之外的闭包

mysql - 将两个 SQL 查询合二为一

tcp - 基于 Mgo 的应用程序代码结构处理连接池和 tcp 超时

go - 在标准库中查找类型

go - new() 和 "regular"分配有区别吗?

ios - 将采用转义闭包的闭包传递给接受该类型闭包的函数的问题