Introduction to Golang Channel usage

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

Channel is a very interesting feature in Golang, and most of the time the channel is used in conjunction with Goroutine. This article mainly introduces some interesting usages of channel.

通道(channel), such as channels (pipelines), which can be used to communicate between the processes by sending typed data, can avoid the pits caused by all memory sharing, and the communication mode of the channel guarantees the synchronization. Data through the channel: only one process at a time can access the data: So there is no competition for data, so design. The attribution of the data (the ability to read and write data) is passed.

A channel is actually a queue of typed messages: the data is transferred. It is a first-out (FIFO) structure so that the order of the elements sent to them can be guaranteed (some people know that the channel can be likened to a bidirectional pipe (tw-way pipe) in Unix shells). The channel is also a reference type, so we use a make() function to allocate memory to it.

Second, Go Channel basic operation syntax

The basic operating syntax for Go channel is as follows:

c: = Make (chan bool)//Create a non-buffered bool channel
C <-x//Send a value to a channel
<-c//Receive a value from a channel
x = <-c//receives a value from channel C and stores it in X
X, OK = <-C//receives a value from the channel, if the channel is closed or no data, then OK will be set to False

By default, communication is synchronous and unbuffered: The send does not end until the recipient receives the data. Imagine a unbuffered channel when there is no space to hold the data: A receiver must have the data ready to receive the channel and the sender can send the data directly to the receiver. So the Send/receive operation of the channel is blocked before the other party is ready:

1) for the same channel, the sending operation (in the coprocessor or function) is blocked until the receiver is ready: if the data in CH is not received, no more data can be passed to the channel: the new input cannot be passed in without the channel being empty. So the send operation waits for CH to become available again: When the channel value is received (the variable can be passed in).

2) for the same channel, the receive operation is blocked (in the process or function) until the sender is available: if there is no data in the channel, the receiver is blocked.

Iii. the channel used as a signal (Signal) scene (semaphore)

1. Wait for an event

Wait for an event. For example:

Package Main

Import "FMT"

Func Main () {
Fmt. Println ("Begin doing something!")
c: = Make (chan bool)
Go func () {
Fmt. Println ("Doing something ...")
Close (c)
}()
<-c
Fmt. Println ("done!")
}

Here main goroutine through "<-c" to wait for "completion event" in Sub Goroutine, sub Goroutine to promote this event through close channel. Of course, you can also act as an event notification by writing a bool value to the channel. Main Goroutine blocks the wait when there is no data readable on channel C.

2. Collaboration with multiple Goroutines

Ditto, the close channel can also be used to collaborate on multiple goroutines, such as the following example, we created 100 worker Goroutine, which are blocked in the "<-start" after being created , until we give the start signal in main goroutine : "Close (start)", these goroutines begin to actually run concurrently.

Testwaitevent2.go
Package Main

Import "FMT"

Func worker (start chan bool, index int) {
<-start
Fmt. Println ("This is Worker:", index)
}

Func Main () {
Start: = Make (chan bool)
For i: = 1; I <= 100; i++ {
Go worker (start, i)
}
Close (start)
Select {}//deadlock we expected
}

3. Select

Getting a value from a different concurrent execution can be accomplished by a keyword that is select very similar to a switch control statement (Section 5.3) is also known as a communication switch; it behaves like a "You're ready" polling mechanism; listen to the select data entering the channel, or it can be the time to send the value with the channel.

selectTo do is to select one of the multiple communication scenarios listed for processing.

    • If it's blocked, it waits until one of them can handle it.
    • If more than one can handle, randomly select a
    • If no channel operation can be processed and the default statement is written, it executes: it will default always be operational (that is, ready to execute).

The following is the basic operation of SELECT.

Select {
Case x: = <-Somechan:
... Use X for some action

Case Y, OK: = <-Someotherchan:
... Use Y for some action,
Check the OK value to determine if the Someotherchan has been closed

Case Outputchan <-Z:
When the Z-value is successfully sent to the channel

Default
... This branch is executed when none of the above case can communicate
}

I think John Graham-cumming here mainly wants to tell us the practice usage of the default branch of SELECT.

1. Select for non-blocking receive

idle:= make (Chan []byte, 5)//Use a buffered channel to construct a simple queue

Select {
Case B = <-idle://Attempt to read from the idle queue
...
Default://Queue empty, assign a new buffer
Makes + = 1
b = Make ([]byte, size)
}

2. Select for non-blocking send

idle:= make (Chan []byte, 5)//Use a buffered channel to construct a simple queue

Select {
Case Idle <-B://Try inserting a buffer into the queue
...
Default://Queue full?

}

"Customary law: For/select"

When we use SELECT, we seldom just evaluation it once, we often use it in conjunction with for {} and choose the appropriate time to exit from for{}.

for {
Select {
Case x: = <-Somechan:
... Use X for some action

Case Y, OK: = <-Someotherchan:
... Use Y for some action,
Check the OK value to determine if the Someotherchan has been closed

Case Outputchan <-Z:
When the Z-value is successfully sent to the channel

Default
... This branch is executed when none of the above case can communicate
}
}

"End Workers"

The following is a common method of terminating sub worker Goroutines, where each worker goroutine a die channel through select to get the exit notification of main goroutine in a timely manner.

Testterminateworker1.go
Package Main

Import (
"FMT"
"Time"
)

Func worker (Die chan bool, index int) {
Fmt. Println ("Begin:this is Worker:", index)
for {
Select {
Case XX:
The branch of doing things
Case <-die:
Fmt. Println ("Done:this is Worker:", index)
Return
}
}
}

Func Main () {
Die: = Make (chan bool)

For i: = 1; I <= 100; i++ {
Go worker (die, I)
}

Time. Sleep (time. Second * 5)
Close (die)
Select {} //deadlock We expected
}

"End Validation"

Sometimes after a worker is terminated, main Goroutine wants to confirm that the worker routine is actually exiting, using the following method:

Testterminateworker2.go
Package Main

Import (
"FMT"
"Time"
)

Func worker (Die chan bool) {
Fmt. Println ("Begin:this is Worker")
for {
Select {
Case XX:
The branch of doing things
Case <-die:
Fmt. Println ("Done:this is Worker")
Die <-True
Return
}
}
}

Func Main () {
Die: = Make (chan bool)

Go Worker (die)

Die <-True
<-die
Fmt. Println ("Worker Goroutine has been terminated")
}

"The closed channel will never block."

Channels can be explicitly closed, although they are different from files: they do not have to be closed every time. You need to close the channel only when you need to tell the recipient that no new values will be provided. Only the sender needs to close the channel, the receiver will never need it.

The following shows the results of reading and writing on a channel that has been closed:

Testoperateonclosedchannel.go
Package Main

Import "FMT"

Func Main () {
CB: = Make (chan bool)
Close (CB)
x: = <-CB
Fmt. Printf ("% #v \ n", X)

X, OK: = <-CB
Fmt. Printf ("% #v% #v \ n", X, OK)

CI: = make (chan int)
Close (CI)
Y: = <-ci
Fmt. Printf ("% #v \ n", y)

CB <-True
}

$go Run Testoperateonclosedchannel.go
False
False false
0
Panic:runtime Error:send on closed channel

You can see that a read operation is performed on an already close unbuffered channel, returning a 0 value of the channel corresponding type, such as the BOOL channel returning the False,int channel returns 0. But writing to the close channel will trigger panic. However, neither reading nor writing can cause blocking.

"Close the channel with the cache"

What happens when you switch unbuffered channel to buffered channel? Let's look at the following example:

Testclosedbufferedchannel.go
Package Main

Import "FMT"

Func Main () {
c: = make (chan int, 3)
C <-15
C <-34
C <-65
Close (c)
Fmt. Printf ("%d\n", <-c)
Fmt. Printf ("%d\n", <-c)
Fmt. Printf ("%d\n", <-c)
Fmt. Printf ("%d\n", <-c)

C <-1
}

$go Run Testclosedbufferedchannel.go
15
34
65
0
Panic:runtime Error:send on closed channel

You can see that the channel with the buffer is slightly different. Although it is close, we can still read the 3 values written before closing. On the fourth read, the 0 value of the channel type is returned. Writing to this type of channel also triggers panic.

Iv. Hidden State (self-growing ID generator)

Here's an example of how the channel is used to hide the state:

1. Example: Unique ID Service

Testuniqueid.go
Package Main

Import "FMT"

Func newuniqueidservice () <-chan string {
ID: = Make (Chan string)
Go func () {
var counter int64 = 0
for {
ID <-fmt. Sprintf ("%x", counter)
Counter + = 1
}
}()
Return ID
}
Func Main () {
ID: = Newuniqueidservice ()
For I: = 0; I < 10; i++ {
Fmt. Println (<-id)
}
}

The newuniqueidservice is associated with the main goroutine via a channel, and the main goroutine does not need to know the details of the UniqueID implementation and the current state, just get the latest ID through the channel.

V. Default (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.

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".

VI. Nil Channels

1, nil channels blocking

The read and write operation of a channel without initialization will be blocked, as in the following example:

Package Main

Func Main () {
var c Chan int
<-c
}

$go Run Testnilchannel.go
Fatal Error:all Goroutines is asleep–deadlock!

Package Main

Func Main () {
var c Chan int
C <-1
}

$go Run Testnilchannel.go
Fatal Error:all Goroutines is asleep–deadlock!

2. Nil channel is useful in select

Look at the following example:

Testnilchannel_bad.go
Package Main

Import "FMT"
Import "Time"

Func Main () {
var C1, C2 chan int = make (chan int), make (chan int)
Go func () {
Time. Sleep (time. Second * 5)
C1 <-5
Close (C1)
}()

Go func () {
Time. Sleep (time. Second * 7)
C2 <-7
Close (C2)
}()

for {
Select {
Case x: = <-c1:
Fmt. PRINTLN (x)
Case x: = <-c2:
Fmt. PRINTLN (x)
}
}
Fmt. Println ("Over")
}

We originally expected the program to output 5 and 72 numbers alternately, but the actual output was:

5
0
0
0
... ... 0 dead Loop

Then carefully analyze the code, the original select each time in case order evaluate:
– The front 5s,select has been blocked;
– Section 5S,C1 returns a 5 after being close, "case x: = <-c1" This branch returns, select Output 5, and re-select
– The next select is also starting from the "case x: = <-C1" Branch evaluate, because C1 is close, according to the previous knowledge, close channel will not block, we will read out the channel corresponding type of 0 value, this is 0 The select outputs 0 again, and even if C2 returns, the program does not go to the C2 branch.
– And so on, program infinite loop output 0

We use the nil channel to improve the program to achieve our intentions, the code is as follows:

Testnilchannel.go
Package Main

Import "FMT"
Import "Time"

Func Main () {
var C1, C2 chan int = make (chan int), make (chan int)
Go func () {
Time. Sleep (time. Second * 5)
C1 <-5
Close (C1)
}()

Go func () {
Time. Sleep (time. Second * 7)
C2 <-7
Close (C2)
}()

for {
Select {
Case x, OK: = <-c1:
If!ok {
C1 = Nil
} else {
Fmt. PRINTLN (x)
}
Case x, OK: = <-c2:
If!ok {
C2 = Nil
} else {
Fmt. PRINTLN (x)
}
}
If C1 = = Nil && C2 = = Nil {
Break
}
}
Fmt. Println ("Over")
}

$go Run Testnilchannel.go
5
7
Over

It can be seen that the next select will block on the channel by placing the closed channel to nil, allowing the Select to continue with the following branch evaluation.

Seven, timers (timeout timer)

1. Time-out mechanism timeout

A select with a time-out mechanism is a regular tip, and here is the sample code that implements the 30s timeout select:

Func worker (start chan bool) {
Timeout: = time. After (* time. Second)
for {
Select {
... do some stuff
Case <-Timeout:
Return
}
}
}

2, Heartbeat Heartbeart

Similar to the timeout implementation, here is a simple heartbeat Select implementation:

Func worker (start chan bool) {
Heartbeat: = time. Tick (* time. Second)
for {
Select {
... do some stuff
Case <-Heartbeat:
... do heartbeat stuff
}
}
}

Reference from:

http://blog.csdn.net/erlib/article/details/44097291

http://tonybai.com/2014/09/29/a-channel-compendium-for-golang/

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.