英文:
Confusing about channel control flow
问题
我正在为你翻译以下内容:
我正在尝试编写一个事件监听器,并尝试在监听器内部控制状态流程。
我知道我在使用通道的原则方面有些遗漏,代码可能看起来很愚蠢。然而,如果有人能帮助我理解我的错误在哪里以及如何改进它,我将不胜感激。
这段代码无法工作:
package main
import (
"fmt"
"time"
)
type A struct {
count int
ch chan bool
exit chan bool
}
func (this *A) Run() {
for {
select {
case <-this.ch:
this.handler()
case <-this.exit:
return
default:
time.Sleep(20 * time.Millisecond)
}
}
}
func (this *A) handler() {
println("hit me")
if this.count > 2 {
this.exit <- true
}
fmt.Println(this.count)
this.count += 1
}
func (this *A) Hit() {
this.ch <- true
}
func main() {
a := &A{}
a.ch = make(chan bool)
a.exit = make(chan bool)
go a.Hit()
go a.Hit()
go a.Hit()
go a.Hit()
a.Run()
fmt.Println("s")
}
它会引发错误:
hit me
0
hit me
1
hit me
2
hit me
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.(*A).handler(0x2101bf000)
/Users/yeer/go/src/github.com/athom/practice/channel-controll.go:31 +0x60
main.(*A).Run(0x2101bf000)
/Users/yeer/go/src/github.com/athom/practice/channel-controll.go:19 +0x66
main.main()
/Users/yeer/go/src/github.com/athom/practice/channel-controll.go:50 +0xed
exit status 2
然而,这段代码可以工作:
package main
import (
"fmt"
"time"
)
type A struct {
count int
ch chan bool
exit chan bool
}
func (this *A) Run() {
for {
select {
case <-this.ch:
this.handler()
case <-this.exit:
return
default:
time.Sleep(20 * time.Millisecond)
}
}
}
func (this *A) handler() {
println("hit me")
}
func (this *A) Hit() {
this.ch <- true
if this.count > 2 {
this.exit <- true
}
fmt.Println(this.count)
this.count += 1
}
func main() {
a := &A{}
a.ch = make(chan bool)
a.exit = make(chan bool)
go a.Hit()
go a.Hit()
go a.Hit()
go a.Hit()
a.Run()
fmt.Println("s")
}
为什么不能在同一级别的通道处理程序中触发另一个通道?
英文:
I am trying to write a event listener and try to control a state flow inside the listener.
I knew I miss some principle of the usage of channel, and the code may looks stupid. However, I will be appreciated if someone can help me to understand what my mistake is and how to improve it.
This code can not work:
package main
import (
"fmt"
"time"
)
type A struct {
count int
ch chan bool
exit chan bool
}
func (this *A) Run() {
for {
select {
case <-this.ch:
this.handler()
case <-this.exit:
return
default:
time.Sleep(20 * time.Millisecond)
}
}
}
func (this *A) handler() {
println("hit me")
if this.count > 2 {
this.exit <- true
}
fmt.Println(this.count)
this.count += 1
}
func (this *A) Hit() {
this.ch <- true
}
func main() {
a := &A{}
a.ch = make(chan bool)
a.exit = make(chan bool)
go a.Hit()
go a.Hit()
go a.Hit()
go a.Hit()
a.Run()
fmt.Println("s")
}
it raise error:
hit me
0
hit me
1
hit me
2
hit me
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.(*A).handler(0x2101bf000)
/Users/yeer/go/src/github.com/athom/practice/channel-controll.go:31 +0x60
main.(*A).Run(0x2101bf000)
/Users/yeer/go/src/github.com/athom/practice/channel-controll.go:19 +0x66
main.main()
/Users/yeer/go/src/github.com/athom/practice/channel-controll.go:50 +0xed
exit status 2
However, this code works:
package main
import (
"fmt"
"time"
)
type A struct {
count int
ch chan bool
exit chan bool
}
func (this *A) Run() {
for {
select {
case <-this.ch:
this.handler()
case <-this.exit:
return
default:
time.Sleep(20 * time.Millisecond)
}
}
}
func (this *A) handler() {
println("hit me")
}
func (this *A) Hit() {
this.ch <- true
if this.count > 2 {
this.exit <- true
}
fmt.Println(this.count)
this.count += 1
}
func main() {
a := &A{}
a.ch = make(chan bool)
a.exit = make(chan bool)
go a.Hit()
go a.Hit()
go a.Hit()
go a.Hit()
a.Run()
fmt.Println("s")
}
Why can not trigger another channel inside a same level channel handler?
答案1
得分: 0
你的代码发生了死锁,因为当你在退出通道 this.exit <- true
上发送消息时,这是在同一个 goroutine 中接收该通道的消息,而且没有办法完成。
可能最明智的做法是用布尔标志替换退出通道。如果你这样做,代码就可以正常工作。
type A struct {
count int
ch chan bool
exit bool
}
func (this *A) Run() {
for !this.exit {
select {
case <-this.ch:
this.handler()
default:
time.Sleep(20 * time.Millisecond)
}
}
}
func (this *A) handler() {
println("hit me")
if this.count > 2 {
this.exit = true
}
fmt.Println(this.count)
this.count += 1
}
func (this *A) Hit() {
this.ch <- true
}
func main() {
a := &A{}
a.ch = make(chan bool)
go a.Hit()
go a.Hit()
go a.Hit()
go a.Hit()
a.Run()
fmt.Println("Done")
}
另一种选择是在每个处理程序中使用单独的 goroutine 运行 go this.handler()
func (this *A) Run() {
for {
select {
case <-this.ch:
go this.handler() // 在这里添加 go
case <-this.exit:
return
default:
time.Sleep(20 * time.Millisecond)
}
}
}
最后,你可以给退出通道添加缓冲区
func main() {
a := &A{}
a.ch = make(chan bool)
a.exit = make(chan bool, 5) // 在这里添加缓冲区
go a.Hit()
go a.Hit()
go a.Hit()
go a.Hit()
a.Run()
fmt.Println("Done")
}
英文:
Your code deadlocks because when you send on the exit channel this.exit <- true
that is from the same goroutine that you are receiving from that channel and there is no way that will ever complete.
Probably the most sensible thing to do is to replace the exit channel with a boolean flag. If you do that then it works fine.
type A struct {
count int
ch chan bool
exit bool
}
func (this *A) Run() {
for !this.exit {
select {
case <-this.ch:
this.handler()
default:
time.Sleep(20 * time.Millisecond)
}
}
}
func (this *A) handler() {
println("hit me")
if this.count > 2 {
this.exit = true
}
fmt.Println(this.count)
this.count += 1
}
func (this *A) Hit() {
this.ch <- true
}
func main() {
a := &A{}
a.ch = make(chan bool)
go a.Hit()
go a.Hit()
go a.Hit()
go a.Hit()
a.Run()
fmt.Println("Done")
}
Another alternative would be to run each handler in its own go routine with go this.handler()
func (this *A) Run() {
for {
select {
case <-this.ch:
go this.handler() // add go here
case <-this.exit:
return
default:
time.Sleep(20 * time.Millisecond)
}
}
}
And finally you could buffer the exit channel
func main() {
a := &A{}
a.ch = make(chan bool)
a.exit = make(chan bool, 5) // add buffer here
go a.Hit()
go a.Hit()
go a.Hit()
go a.Hit()
a.Run()
fmt.Println("Done")
}
答案2
得分: 0
你应该在循环中使用Time.After()来进行等待:
select {
case <-this.ch:
go this.handler() // 在这里添加go关键字
case <-this.exit:
return
case <-time.After(20 * time.Millisecond)
英文:
You should use the Time.After() for the wait in the loop:
select {
case <-this.ch:
go this.handler() // add go here
case <-this.exit:
return
case <-time.After(20 * time.Millisecond)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论