混淆了渠道控制流程

时间:2013-11-17 06:27:31

标签: go channel

我正在尝试编写一个事件侦听器并尝试控制侦听器内的状态流。 我知道我错过了一些使用频道的原则,代码可能看起来很愚蠢。但是,如果有人能帮我理解我的错误以及如何改进它,我将不胜感激。

此代码无效:

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")
}

为什么不能在同一级别的通道处理程序中触发另一个通道?

2 个答案:

答案 0 :(得分:0)

您的代码死锁,因为当您发送退出渠道this.exit <- true时,该渠道来自您从该渠道收到的同一个goroutine,并且无法完成。

可能最明智的做法是用布尔标志替换退出通道。如果你这样做那么它可以正常工作。

Play

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")
}

另一种方法是使用go this.handler()

在自己的go例程中运行每个处理程序

Play

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)
        }
    }
}

最后你可以缓冲退出渠道

Play

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")
}

答案 1 :(得分:0)

你应该在循环中使用Time.After()进行等待:

    select {
    case <-this.ch:
        go this.handler() // add go here
    case <-this.exit:
        return
    case <-time.After(20 * time.Millisecond)