Golang Funny: Play with Channel

mikespook · · 4573 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Golang have a funny toy: Channel. Here has some words from the specification:

A channel provides a mechanism for two concurrently executing functions to synchronize execution and communicate by passing a value of a specified element type.

Yep! It is boring and dull. You can’t understand it when you read it first time. Actually, you can think of it as a PIPE or a FIFO queue. It’s lightweight and simple. Channel is not of Golang origin. It also appears in some other languages(embedded). And most of time, it’s a function as a part of a huge, complexly and heavy message queue system.

OK! Let’s have some fun.

Common way: Producer / Consumer

This is the most common way to use a channel. A producer makes things and put them into the channel. And a consumer get them from the channel. On by on, in order. If the channel is full, the producer must wait. Otherwise, if the channel is empty, the consumer must wait.

You can get full source from here. Running it at local is better.

Producer:

func producer(c chan int64, max int) {
    defer close(c)
    for i:= 0; i < max; i ++ {
        c <- time.Now().Unix()
    }
}

The producer makes a number of ‘max’ int64, and put them into the channel ‘c’. Notice that, here is a defer for closing the channel.

Consumer:

func consumer(c chan int64) {
    var v int64
    ok := true
    for ok {
        if v, ok = <-c; ok {
            fmt.Println(v)
        }
    }
}

Read int64s from the channel one by one, and print them on to the screen. When the channel closed, the variable ‘ok’ will be set to ‘false’.

Auto-increment ID generator

If we let the producer makes a sequence of integer. It will be a auto-increment ID generator. I wrote a package that encapsulates it. You can found it at here. The usage can be found at here.

type AutoInc struct {
    start, step int
    queue chan int
    running bool
}

func New(start, step int) (ai *AutoInc) {
    ai = &AutoInc{
        start: start,
        step: step,
        running: true,
        queue: make(chan int, 4),
    }
    go ai.process()
    return
}

func (ai *AutoInc) process() {
    defer func() {recover()}()
    for i := ai.start; ai.running ; i=i+ai.step {
        ai.queue <- i
    }
}

func (ai *AutoInc) Id() int {
    return <-ai.queue
}

func (ai *AutoInc) Close() {
    ai.running = false
    close(ai.queue)
}

Semaphore

Semaphore is a funny usage of Channel. Here is a demo from “Effective Go“. You should have read it, haven’t you? If not, read it now…

I have used the channel as a semaphore in the gearman-go, a package for Gearman job server. You can found the codes here. Line 232, the Worker.exec will be blocked by Worker.limit.

var sem = make(chan int, MaxOutstanding)

func handle(r *Request) {
    sem <- 1    // Wait for active queue to drain.
    process(r)  // May take a long time.
    <-sem       // Done; enable next request to run.
}

func Serve(queue chan *Request) {
    for {
        req := <-queue
        go handle(req)  // Don't wait for handle to finish.
    }
}

Randomized sequence

Of course, we could modify the auto-increment ID generator. Let the producer makes randomized number and put into the channel. But, it’s not funny enough. Right?

Here is another randomized sequence. It was inspired by the specification. It produces 0 or 1 randomly:

func producer(c chan int64, max int) {
    defer close(c)
    for i:= 0; i < max; i ++ {
        select { // randomized select
            case c <- 0:
            case c <- 1:
        }
    }
}

The timeout timer

If a channel was blocked at read/write, it will be blocked forever. Until the channel was closed, then a panic raised. The channel has no timeout timer build in. And it looks like there is no plan to add a timer into channel. But most of time, we need a timeout mechanism. Eg. a producer executed, and something wrong with it, then nothing put into the channel. The consumer was blocked now, until the channel was closed. Close the channel for every time? No! That’s not a good idea.

Here is a solution.

    c := make(chan int64, 5)
    defer close(c)
    timeout := make(chan bool)
    defer close(timeout)
    go func() {
        time.Sleep(time.Second) // wait a second
        timeout <- true // put a bool value into the channel
    }()
    select {
        case <-timeout: // timeout
            fmt.Println("timeout...")
        case <-c: // received a data
            fmt.Println("Read a data.")
    }

Have you noticed the select statement? Which channel receive data, which sub-branch execution. So … do we need more explanation?

This also used in the client of gearman-go, line 238.

Further: Thx @mjq. The time.After is really better solution. And I read the source codes at src/pkg/time/sleep.go, line 74. Internal implementation is same as the above.

One more thing

This is not only a introduction about golang channel, but also a English writing exercise. So, if you have some other ideas about golang channel usage, share them with me! If you found some issues in my blog, plz point out them for me. thx!

[2012.06.26] updated!
Using close to broadcast


有疑问加站长微信联系(非本文作者)

本文来自:mikespook 的博客

感谢作者:mikespook

查看原文:Golang Funny: Play with Channel

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

4573 次点击  
加入收藏 微博
1 回复  |  直到 2000-01-01 00:00:00
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传