从有和没有 goroutine 的渠道了解 select
Understanding select from channels with and without goroutine
你好 Whosebug 社区,
我正在使用 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 中的闭包 运行ning 并且 运行 它作为
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}
并且没有从频道接收并继续等待。
我了解到这可能是由于非缓冲通道引起的。因此,为了验证这一点,我修改了库以使用缓冲通道,并且生成新观察者的函数部分看起来像
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 中从通道读取有效,而没有 goroutine 就不起作用吗?
谢谢。
我不熟悉 fsnotify,但看起来它不会在频道上发送任何东西,直到调用 Watcher.Add()
来监视文件,但 Watcher.Add()
出现在 select
在第二个版本中。在这种情况下,select 将永远阻塞,因为没有任何信号可以发出,因为 Watcher.Add()
没有被调用。于是就出现了死锁。
你好 Whosebug 社区, 我正在使用 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 中的闭包 运行ning 并且 运行 它作为
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}
并且没有从频道接收并继续等待。
我了解到这可能是由于非缓冲通道引起的。因此,为了验证这一点,我修改了库以使用缓冲通道,并且生成新观察者的函数部分看起来像
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 中从通道读取有效,而没有 goroutine 就不起作用吗?
谢谢。
我不熟悉 fsnotify,但看起来它不会在频道上发送任何东西,直到调用 Watcher.Add()
来监视文件,但 Watcher.Add()
出现在 select
在第二个版本中。在这种情况下,select 将永远阻塞,因为没有任何信号可以发出,因为 Watcher.Add()
没有被调用。于是就出现了死锁。