Go Channel Explanation

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

The channel is a core type in go, and you can think of it as a conduit through which the core unit can send or receive data for communication (communication).

Its operator is the arrow <-.

ch <- v    // 发送值v到Channel ch中v := <-ch  // 从Channel ch中接收数据,并将数据赋值给v

(arrow pointing is the flow of data)

Just like the map and slice data types, the channel must first be created and reused:

ch := make(chan int)

Channel type

The channel type is defined in the following format:

ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .

It includes three types of definitions. The optional <-represents the direction of the channel. If you do not specify a direction, then the channel is bidirectional, either to receive data or to send data.

chan T          // 可以接收和发送类型为 T 的数据chan<- float64  // 只可以用来发送 float64 类型的数据<-chan int      // 只可以用来接收 int 类型的数据

<-is always preferred and the leftmost type is combined. (The <-operator associates with the leftmost Chan possible)

chan<- chan int    // 等价 chan<- (chan int)chan<- <-chan int  // 等价 chan<- (<-chan int)<-chan <-chan int  // 等价 <-chan (<-chan int)chan (<-chan int)

Use make to initialize the channel, and you can set the capacity:

make(chan int, 100)

The capacity (capacity) represents the maximum number of elements that the channel holds, representing the size of the channel's cache. If no capacity is set, or if the capacity is set to 0, the channel is not cached, and only sender and receiver are ready for their communication (communication) to occur (Blocking). If the cache is set, it is possible that the send will not block until the buffer is full, and the receive will not block until the cache is empty. A nil channel does not communicate.

The channel can be closed by the built-in close method.

You can receive/send data in multiple goroutine from/to a channel without having to consider additional synchronization measures.

The channel can act as a first-in, first-out (FIFO) queue, and the order in which the data is received and sent is consistent.

The channel's receive supports multi-valued assignment, such as

v, ok := <-ch

It can be used to check if the channel has been closed.

Send statement

The Send statement is used to send data to the channel, such as CH <-3. It is defined as follows:

SendStmt = Channel "<-" Expression .Channel  = Expression .

The channel and expression must be evaluated before the communication (communication) begins (evaluated), such as the following (3+4) first calculates 7 and then sends it to the channel.

c := make(chan int)defer close(c)go func() { c <- 3 + 4 }()i := <-cfmt.Println(i)

Send pre-execution (proceed) communication (communication) has been blocked. As previously stated, a non-cached channel will only be executed when receiver is ready. If there is a cache and the cache is not full, send will be executed.

Continuing to send data to a channel that has already been close will cause run-time panic.

Sending data to the nil channel is consistently blocked.

Receive operator

The <-ch is used to receive data from channel CH, which is always blocked until data can be received. Receiving data from a nil channel will always be block.

Receiving data from a closed channel is not blocked, but returns immediately, and returns 0 of the element type (zero value) after receiving the sent data.

As mentioned earlier, you can use an additional return parameter to check if the channel is closed.

x, ok := <-chx, ok = <-chvar x, ok = <-ch

If OK is false, indicates that the received X is the resulting 0 value, the channel is closed or empty.

Blocking

By default, send and receive are blocked until the other party is ready. This method can be used to synchronize in Gororutine without using the displayed lock or condition variable.

As in the official example x, y: = <-c, <-c this sentence will wait until the result of the calculation is sent to the channel.

import "fmt"func sum(s []int, c chan int) {   sum := 0 for _, v := range s {      sum += v }    c <- sum // send sum to c}func main() {    s := []int{7, 2, 8, -9, 4, 0}   c := make(chan int) go sum(s[:len(s)/2], c)   go sum(s[len(s)/2:], c)   x, y := <-c, <-c // receive from c fmt.Println(x, y, x+y)}

Buffered Channels

The second parameter of make specifies the size of the cache: ch: = make (chan int, 100).

By using the cache, you can avoid blocking and provide the performance of your application.

Range

The for ... range statement can handle channel.

 go func() {        time.Sleep(1 * time.Hour)    }()  c := make(chan int) go func() {        for i := 0; i < 10; i = i + 1 {          c <- i        }        close(c)   }()   for i := range c {     fmt.Println(i)   } fmt.Println("Finished")}

The iteration value generated by range C is the value sent in the channel, and it will iterate until the channel is closed. In the above example, if Close (c) is commented out, the program will always block the line in the for ... range.

Select

The SELECT statement selects a set of possible send operations and receive operations to handle. It is similar to switch, but is only used to handle communication (communication) operations. It can be a send statement, a receive statement, or default.

The Receive statement can assign a value to one or two variables. It must be a receive operation.

A maximum of one default case is allowed, which can be placed anywhere in the case list, although most of us will put it at the end.

import "fmt"func fibonacci(c, quit chan int) {  x, y := 0, 1 for {       select {        case c <- x:         x, y = y, x+y        case <-quit:         fmt.Println("quit")          return      }    }}func main() {    c := make(chan int) quit := make(chan int)  go func() {        for i := 0; i < 10; i++ {            fmt.Println(<-c)      }        quit <- 0 }()  fibonacci(c, quit)}

If there is more than one case to deal with, such as having multiple channel to receive data at the same time, go will randomly select an instance processing (pseudo-random). If no case needs to be handled, the default is chosen to handle if it exists. If there is no default case, the SELECT statement blocks until one of the processing needs to be processed.

It should be noted that the operation on the nil channel will always be blocked, and if there is no default case, only the select of nil channel will be blocked.

The SELECT statement, like the switch statement, is not a loop, it only chooses a case to handle, and if you want to handle the channel all the time, you can add an infinite for loop outside:

for { select {    case c <- x:     x, y = y, x+y    case <-quit:     fmt.Println("quit")      return  }}

Timeout

A very important application of select is time-out processing. Because we mentioned above, if there is no case to deal with, the SELECT statement will always be blocked. At this point we may need a timeout operation to handle the timeout situation. In this example we will send a data to channel C1 in 2 seconds, but select is set to 1 seconds timeout, so we will print timeout 1 instead of result 1.

import "time"import "fmt"func main() {    c1 := make(chan string, 1)    go func() {        time.Sleep(time.Second * 2)        c1 <- "result 1"    }()    select {    case res := <-c1:        fmt.Println(res)    case <-time.After(time.Second * 1):        fmt.Println("timeout 1")    }}

In fact, it uses time. After method, it returns a one-way channel of type <-chan time, at a specified time, to the returned channel.

Timer and Ticker

Let's take a look at the two channel of time. A timer is a timer that represents a single event in the future, and you can tell the timer how long it will take you to wait, it provides a channel, and at that time in the future the channel provides a time value. In the following example, the second row blocks for about 2 seconds, until the time is up for the execution to continue.

timer1 := time.NewTimer(time.Second * 2)<-timer1.Cfmt.Println("Timer 1 expired")

Of course, if you just want to wait, you can use time. Sleep to achieve.

You can also use a timer. Stop to halt the timer.

timer2 := time.NewTimer(time.Second)go func() {   <-timer2.C    fmt.Println("Timer 2 expired")}()stop2 := timer2.Stop()if stop2 {  fmt.Println("Timer 2 stopped")}

Ticker is a timed-triggered timer that sends an event (the current time) to the channel at an interval (interval), while the receiver of the channel can read events from the channel at a fixed interval. In the following example ticker is triggered every 500 milliseconds, you can observe the time of the output.

ticker := time.NewTicker(time.Millisecond * 500)go func() {   for t := range ticker.C {      fmt.Println("Tick at", t)    }}()

Like a timer, ticker can also be stopped by the Stop method. Once it stops, the receiver will no longer receive data from the channel.

Close

The built-in close method can be used to close the channel.

Summarize the receiver operation of sender after the channel is closed. If channel C has been closed, continuing to send data to it will cause panic:send on closed channel:

import "time"func main() {   go func() {       time.Sleep(time.Hour)    }()  c := make(chan int, 10)    c <- 1  c <- 2  close(c)   c <- 3}

However, from this closed channel, not only can read out the sent data, but also can continuously read the value of 0:

c := make(chan int, 10)c <- 1c <- 2close(c)fmt.Println(<-c) //1fmt.Println(<-c) //2fmt.Println(<-c) //0fmt.Println(<-c) //0

However, if you read through range, the for loop will jump out after the channel is closed:

c := make(chan int, 10)c <- 1c <- 2close(c)for i := range c {   fmt.Println(i)}

Through I, OK: = <-c can see the status of the channel and determine whether the value is a value of 0 or normal read.

c := make(chan int, 10)close(c)i, ok := <-cfmt.Printf("%d, %t", i, ok) //0, false

Synchronous

The channel can be used for synchronization between Goroutine. In the following example, main goroutine waits for the worker to complete the task by doing the channel. When the worker finishes the task, it simply sends a data to the channel to notify the main Goroutine task to complete.

import (  "fmt"    "time")func worker(done chan bool) {    time.Sleep(time.Second)   // 通知任务已完成  done <- true}func main() {    done := make(chan bool, 1)  go worker(done)  // 等待任务完成   <-done}
Related Article

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.