這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
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