Golang Funny: Play with Channel

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

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

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.