Channel1. Overview
"Network, concurrency" is the two major feature of the Go language. The Go language is known as the "Internet C language". Compared with the traditional C language, writing a server uses less and simpler code. Writing a server is concurrent in addition to the network. Compared with other languages such as Python, go supports concurrency to improve its performance.
Goroutine and channel are two core feature of go in terms of "concurrency.
A channel is a way of communication between goroutines. It is similar to a Unix pipeline.
Channel declaration:
Channeltype = ("Chan" | "Chan" "<-" | "<-" "Chan") elementtype.
For example:
VaR ch Chan int
VaR mongochan <-int // the token can only be written.
VaR CH2 <-chan int // CH2 can only be read
Channel is type-related, that is, a channel can only pass one type. For example, the above ch can only pass Int.
In the go language, there are four reference types: slice, MAP, channel, and interface.
Slice, map, and channel are generally initialized through make:
CI: = make (Chan INT) // unbuffered channel of Integers
CJ: = make (Chan int, 0) // unbuffered channel of Integers
CS: = make (chan * OS. File, 100) // buffered channel of pointers to files
When creating a channel, you can provide an optional integer parameter to set the buffer size of the channel. The default value is 0, which is used to build the default "no buffer channel", also known as "synchronous channel ".
As a communication mechanism between goroutine, channel is similar to other communication mechanisms in the operating system. It generally has two purposes: synchronization or message transmission.
2. Synchronization
C: = make (Chan INT) // allocate a channel.
// Start the sort in a goroutine; when it completes, signal on the channel.
Go func (){
List. Sort ()
C <-1 // send a signal; value does not matter.
}()
Dosomethingforawhile ()
<-C // wait for sort to finish; discard sent value.
In the preceding example, the sub-goroutine is used for sorting. The main goroutine can do some other tasks and then wait for the sub-goroutine to complete sorting.
The receiver is blocked until data arrives. If the channel is unbuffered, the sender will be blocked until the receiver extracts the data. If the channel has a buffer, the sender will be blocked until the data is copied to the buffer. If the buffer is full, the sender can recover from the blocking status only after the receiver takes the data.
3. message transmission
Let's simulate the classic producer-consumer model.
Func producer (queue Chan <-int ){
For I: = 0; I <10; I ++ {
Queue <-I
}
}
Func consumer (queue <-chan INT ){
For I: = 0; I <10; I ++ {
V: = <-queue
FMT. println ("receive:", V)
}
}
Func main (){
Queue: = make (Chan int, 1)
Go producer (Queue)
Go consumer (Queue)
Time. Sleep (1e9) // Let the producer and consumer complete
}
The preceding example generates data in the producer and processes the data in the consumer.
4. Server programming model
Programming on the server is a common model: the main thread receives requests, and then distributes the requests to the working thread to complete request processing. Use go as follows:
Func handle (R * request ){
Process (r) // may take a long time.
}
Func serve (queue chan * request ){
For {
Req: = <-queue
Go handle (req) // don't wait for handle to finish.
}
}
Generally, the processing capability of the server is not infinite. Therefore, it is necessary to limit the number of threads (or goroutine. In C/C ++ programming, we generally use semaphores. In go, we can achieve the same effect through channel:
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.
}
}
By introducing the SEM channel, We have restricted running of only maxoutstanding goroutine at the same time. However, the above practice only limits the number of running goroutines, and does not limit the number of goroutines generated. If the request arrives too quickly, a large number of goroutines will be generated, which will completely consume system resources.
Therefore, it is necessary to limit the number of goroutine instances:
Func serve (queue chan * request ){
For Req: = range queue {
SEM <-1
Go func (){
Process (req) // buggy; see explanation below.
<-SEM
}()
}
}
The above Code seems simple and clear, but there is a problem in go. Loop variables in the go language are reused in each iteration. More directly, req is shared among all sub-goroutine. From the perspective of variable scope, the variable req is global for all goroutines.
This problem falls into the scope of language implementation. In C, you should not pass a local variable to another thread for processing. There are many solutions, which are discussed here. Personally, I prefer the following method:
Func serve (queue chan * request ){
For Req: = range queue {
SEM <-1
Go func (R * request ){
Process (r)
<-SEM
} (Req)
}
}
At least, such code won't confuse beginners of a go program. In addition, it is more common sense from the perspective of variable scope.
In actual C/C ++ programming, we tend to create working threads at the beginning, and the number of threads is fixed. In go, we can also do this:
Func handle (queue chan * request ){
For R: = range queue {
Process (r)
}
}
Func serve (clientrequests chan * request, quit Chan bool ){
// Start handlers
For I: = 0; I <maxoutstanding; I ++ {
Go handle (clientrequests)
}
<-Quit // wait to be told to exit.
}
Start a fixed number of handle goroutine at the beginning, and each goroutine directly reads requests from the channel. This method is relatively simple, but I don't know if there is a problem with "surprise group? The goroutine implementation needs to be analyzed in the future.
5. Channel passing the channel
As a native type of the Go language, channel can naturally be passed through the channel. Channel Transmission allows you to easily solve some practical problems.
In the previous section, the main goroutine transmits requests to the working goroutine through a channel. Similarly, we can return the processing result to the primary goroutine through the channel.
Main goroutine:
Type request struct {
ARGs [] int
Resultchan Chan int
}
Request: = & request {[] int {3, 4, 5}, make (Chan INT )}
// Send request
Clientrequests <-Request
// Wait for response.
FMT. printf ("Answer: % d \ n", <-request. resultchan)
The main goroutine sends the request to the request channel and waits for the result channel. After processing the sub-goroutine, write the result to the result channel.
Func handle (queue chan * request ){
For Req: = range queue {
Result: = do_something ()
Req. resultchan <-Result
}
}
6. Multiple Channels
In actual programming, it is often possible to process multiple channels in a goroutine. It is impossible for us to block the two channels. In this case, we should select the field. Similar to the SELECT statement in C language that can monitor multiple FD, select statements in go language can wait for multiple channels.
C1: = make (Chan string)
C2: = make (Chan string)
Go func (){
Time. Sleep (time. Second * 1)
C1 <-"one"
}()
Go func (){
Time. Sleep (time. Second * 2)
C2 <-"two"
}()
For I: = 0; I <2; I ++ {
Select {
Case msg1: = <-C1:
FMT. println ("received", msg1)
Case msg2: = <-C2:
FMT. println ("received", msg2)
}
}
In C, we usually pass a time-out time to the select function. In go, select does not have this parameter, which is equivalent to a time-out value of 0.
References
Https://golang.org/doc/effective_go.html
YY brother
Source: http://www.cnblogs.com/hustcat/
The copyright of this article is shared by the author and the blog Park. You are welcome to repost this article. However, you must retain this statement without the author's consent and provide a clear link to the original article on the article page. Otherwise, you will be held legally liable.
Learn more about golang (2)-Channel