在golang定期冲洗通道

时间:2013-06-24 20:19:15

标签: go

我需要定期刷新频道的内容。 我用len()做了这个,我想知道是否有更好的方法来做到这一点。

http://play.golang.org/p/YzaI_2c_-F

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    commch := make(chan int, 100)
    go fillchan(commch)
    drainchan(commch)
}

func fillchan(commch chan int) {
    for {
        select {
        case <-time.Tick(30 * time.Millisecond):
            commch <- rand.Int()
        }
    }
}

func drainchan(commch chan int) {
    for {
        chanlen := len(commch) // get number of entries in channel
        time.Sleep(1 * time.Second)
        for i := 0; i <= chanlen; i++ { //flush them based on chanlen
            fmt.Printf("chan len: %s num: %s\n", chanlen, <-commch)
        }
    }
}

编辑1:似乎这是更好的方法 http://play.golang.org/p/4Kp8VwO4yl

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    commch := make(chan int, 1000)
    go fillchan(commch)
    for {
        select {
        case <-time.Tick(1000 * time.Millisecond):
            drainchan(commch)
        }
    }
}

func fillchan(commch chan int) {
    for {
        select {
        case <-time.Tick(300 * time.Millisecond):
            commch <- rand.Int()
        }
    }
}

func drainchan(commch chan int) {
    for {
        select {
        case e := <-commch:
            fmt.Printf("%s\n",e)
        default:
            return
        }
    }
}

编辑2:删除了select,防止了内存泄漏。请注意 http://play.golang.org/p/WybAhRE3u4

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    commch := make(chan int, 1000)
    go fillchan(commch)
    for _ = range time.Tick(1000 * time.Millisecond) {
        drainchan(commch)
    }
}

func fillchan(commch chan int) {
    for _ = range time.Tick(300 * time.Millisecond) {
        commch <- rand.Int()
    }
}

func drainchan(commch chan int) {
    for {
        select {
        case e := <-commch:
            fmt.Printf("%s\n", e)
        default:
            return
        }
    }
}

3 个答案:

答案 0 :(得分:6)

需要清除频道内容是不寻常的。频道不提供此功能 - 但你可以制作一个行为方式的goroutine(......如果真的确实想要)。

通常,您会考虑更多关于在一个通道上输入并在另一个通道上输出的goroutine;两个通道都具有相同的数据类型。原则上你可以用这种方式模拟所有缓冲的通道;对于它的客户端,goroutine的行为类似于普通的缓冲通道,因为它传递了它所接收的内容。

在goroutine中添加第三个通道,并在它和输入之间添加 select 。这将允许您在没有竞争条件蔓延的情况下触发清空缓冲区。简单。

现在有三个通道连接到goroutine - 两个输入和一个输出。因此,当您设计将使用它的东西时,您可以推断出刷新数据的语义是什么。

想到一个亲戚。考虑一个带有一个输入和一个输出通道的goroutine。它提供了固定大小的覆盖缓冲区,即即使在输出通道被阻塞时也总是准备从其输入通道读取的缓冲区。这还需要具有默认情况的选择,但不需要第三个通道。覆盖缓冲区有一个明确的用例:当通道和goroutine连接到循环时,很可能发生死锁。覆盖缓冲区作为死锁的一个候选解决方案派上用场,因为一些数据在迟到时是无用的 - 例如,当应用程序太忙而无法响应时,您可以在GUI中丢弃鼠标事件。

答案 1 :(得分:1)

来自https://github.com/eapache/channelsBatchingChannel可以满足您的需求。

答案 2 :(得分:0)

当我遇到这个问题时,我就是这样处理的。

func (s *ReadStream) drain() {
    go func() {
        for b := range s.chan {
            blackhole(b)
        }
    }()
}

func blackhole(b []byte) {}

在你的select可能被阻塞的情况下.Context似乎是错误的选择:

for {
    select {
    case <-ctx.Done():
        return
    default:
        send<-getData()
    }
}

如果发送已满,我们将受到外部goroutine的支配,然后才能收到完成信号。如果您确定消费者将在渠道关闭之前阅读,这是可以的,但如果这些消费者可能会遇到错误情况并返回,那么您就可以做到。在这种特定的情况下,我是一个用内部quit chan和waitgroup替换上下文的粉丝,然后提供一个公共的Kill()方法。当然,只要我绝对确定我可以丢弃数据。

func (s *ReadStream) Kill() {
    s.quit<-struct{}{}
    s.drain()   // ensure goroutine sees the cancel
    s.wg.Wait() // wait for goroutine to see the cancel
    s.close()
}