This is a creation in Article, where the information may have evolved or changed.
Strictly speaking, this article is another piece of my article "Golang Funny:play with Channel" in Chinese version. However, after all, it is used in Chinese as the mother tongue, so it is not translated, re-wrote according to that content.
The channel is a very interesting feature in Golang, and in my experience of using Golang coding, most events will enjoy the pleasure of channel and goroutine coordination. So this article mainly introduces some interesting usages of channel.
Here is a description of the channel in the Go programming language specification of the oling Cat translator:
A channel provides a mechanism for synchronizing between two concurrently executed functions and communicating by passing values that correspond to the type of the channel element.
This description is tedious and boring. When I first read it, I couldn't understand exactly what it was. In fact, the channel can be thought of as a pipe or FIFO queue, very simple and lightweight. The channel is not the Golang initiative. It also appears as a built-in feature in other languages. In most cases, it is a feature of a large, clumsy, and complex Message Queuing system.
Let's get some fun together!
The most common way: producer/Consumer
The producer generates some data to put it into the channel, and then the consumer takes the data out of the channel in order, one by one, for processing. This is the most common way of using channel. When the buffer of the channel is exhausted, the producer must Wait (block). In other words, if there is no data in the channel, consumers will have to wait.
The source code for this example is here. It is best to download to run locally.
Producers
Func producer (c Chan int64, Max int) { defer close (c) for i:= 0; i < max; I + + { c <-time. Now (). Unix () }}
The producer generates a number of "Max" Int64, and puts it in channel "C". It is important to note that the channel is closed with defer when the function is launched.
Consumers
Func consumer (c Chan Int64) { var v int64 OK: = True for OK { if V, ok = <-c; OK { FMT. Println (v) }}}
Read a int64 number from one of the channel, and then print it on the screen. When the channel is closed, the variable "OK" is set to "false".
Self-growing ID generator
When the birth lets the production person can produce the integer sequentially. It is a self-growing ID generator. I have encapsulated this function as a package. and host its code here. Use the example to refer to the code 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) Clos E () { ai.running = False Close (ai.queue)}
Signal Volume
Semaphores are also an interesting application of channel. Here's an example from "efficient Go Programming". You should have read it, haven't you? If not, start reading now ...
I used the semaphore in the API package gearman-go for the Gearman service. In Worker/worker.go 232 rows, the number of parallel worker.exec is reached Worker.limit, it will be blocked.
var sem = make (chan int, maxoutstanding) func handle (R *request) { sem <-1 //wait for release; process (R) ///may require a A long process of processing; <-sem //Finish, release another process. }func Serve (Queue chan *request) { for { req: = <-queue go handle (req) //No wait for handle to complete. }}
Random sequence Generator
Of course, you can modify the self-growth ID generator. Let the producer generate random numbers into the channel. But it's kind of boring, isn't it?
Here is another implementation of the random sequence generator. Inspiration comes from the language specification. It randomly generates a sequence of 0/1:
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: } }}
Timeout timer
When a channel is Read/write blocked, it will be blocked forever until the channel is closed and a panic is generated. The channel has no built-in timer for timeouts. And there seems to be no plan to add such a feature to the channel. But in most cases, we need a timeout mechanism. For example, there was an error in the execution of the producer, so no data was placed into the channel. Consumers will be blocked until the channel is closed. Turn off the channel every time it goes wrong? This is definitely not a good idea.
Here's a solution:
c: = Make (chan int64, 5) defer close (c) timeout: = Make (chan bool) defer close (timeout) go func () { T Ime. Sleep (time. Second)//Wait a second timeout <-true//Put flag in timeout queue } () Select {Case <-timeout://timeout FMT. PRINTLN ("Timeout ...") Case <-c://Received Data FMT. Println ("Read a date.") }
Did you notice the SELECT statement? Which channel has data first, which branch executes first. So...... Do you need more explanations?
This is also used in the client API implementation of Gearman-go, line No. 238.
After the English version of this article was released, @mjq reminded me that I could use time. After. In the project, this is really a better notation. I have to thank him! I also read the 74th line of Src/pkg/time/sleep.go, time. The implementation of After. Its internal implementation is exactly the same as the code above.
And more ...
The various interesting applications mentioned above can of course be implemented in other message queues, but because of the simplicity and lightness of the channel, it makes sense for the Golang channel to realize these interesting features and has real-world applications. In fact, I think the interesting channel usage is much more than that. If you find other interesting gameplay, be sure to let me know. Thank you!