Go language concurrent Programming (ii)

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

The concurrent programming of the go language is mainly realized by Goroutine and channel. The following is a study note, for informational purposes only. Please correct me.

First, Goroutine

(i) Understanding of Goroutine

For beginners, Goroutine directly understand the thread as it should be. When using the Go keyword to invoke a function, when starting a goroutine, it is equivalent to launching a thread, execute this function.
In fact, however, a goroutine is not a thread, but a dispatch unit that is smaller than a thread. By default all goroutines run in one thread, and the goroutines is executed one at a time. If the thread is blocked, it is assigned to an idle thread.

(ii) Use of Goroutine

Very simple to use, add a go before the function
Example: Go f (A, B)//Open, do not wait for its end, the main thread continues to execute.

PS: Note that after a goroutine is turned on, if it does not wait for its execution, main (main Goroutine) will continue to the next step, then the main thread is finished, the Goroutine program will not be executed. How to solve? The code is as follows:

saySomething(str string) {    i:= 0;ii++ {        time.Sleep(time.Millisecond * 1000)        fmt.Println(str)    main() {    // 启动一个goroutine线程    saySomething("Hello")    saySomething("World")}

Why do you sleep here? is to wait for Go saysomething ("Hello") to finish processing.

All right, here we go. A requirement: If you want to artificially set a sleep time, very inconvenient, need to make a goroutine after the end of the automatic transfer of data to the main thread, tell the main thread this goroutine has ended. The concept of channel is introduced here.

Second, channel

(i) Channel concept

Simply put, the main thread tells you that you can open the Goroutine, but I opened a channel in my main thread, after you have done what you want to do, put something in the channel to tell me you have finished, I end the main thread.

(ii) use of channel

1, like map, the channel is a reference type, and make allocates memory. If you provide an optional integer argument when you call make, the channel is allocated a buffer of the appropriate size. The buffer size defaults to 0, which corresponds to a unbuffered channel or a synchronous channel.

make(chanint// 无缓冲整数信道make(chan *os.File, 100// 缓冲的文件指针信道

2, the channel can be used to let the running Goroutine wait for the sorting to complete. Make sure that (Goroutine) is in a known state with each other.
-Operations to insert data into the channel

c1

-output data from the channel

<- c

code example:

make(chanint// Allocate a channel.// 在goroutine中启动排序,当排序完成时,信道上发出信号gofunc() {  list.Sort()  c <- 1// 发送一个信号,值是多少无所谓。// 等待排序完成,丢弃被发送的值。

The recipient (receivers) is blocked until the data is received. If the channel is non-buffered, the sender (sender) is also blocked until the recipient receives the data. If the channel has a buffer, the sender is blocked only before the data is filled in the buffer, and if the buffer is full, it means that the sender waits for a value to be taken by a recipient.

(iii) channel limit throughput

Buffered channels can be used like beacons, for example to limit throughput. In the following example, the incoming request is passed to Handle,handle to send a value to the channel, then processing the request, and finally receiving a value from the channel. The size of the channel buffers limits the number of concurrent calls to process.

varmake(chanint, MaxOutstanding)func handle(r *Request) {  sem <- 1// 等待队列缓冲区非满  // 处理请求,可能会耗费较长时间.  // 请求处理完成,准备处理下一个请求}funcchan *Request) {  for {    req := <-queue    go//不等待handle完成  }}

The same functionality can be achieved by starting a fixed number of handle goroutines, which read requests from the request channel. The number of goroutines limits the number of concurrent calls to process. The serve function also receives an exit signal from a channel, and after the Goroutines is started, it is blocked until the exit signal is received:

funcchan *Request) {  forrange queue {   process(r)  }}funcchanchanbool) {  // 启动请求处理  for i := 0; i < MaxOutstanding; i++ {    go handle(clientRequests)  }  // 等待退出信号}

(d) channel transmission via channel

One of the most important features of Go is that the channel can be allocated memory and passed like other types of values. This feature is commonly used to implement secure and parallel de-multiplexing (demultiplexing).
In the previous example, handle was an idealized function to process the request, but we did not define the exact type of request it could handle. If the type includes a channel, each client can provide its own way to answer

typestruct {  args []int  func([]intint  chanint}

The client provides a function, the parameters of the function, and a channel that the request object uses to receive the answer.

func sum(a []intint) {forrange a {  s += v}return}request := &Request{[]int{3, 4, 5make(chanint)}// 发送请求clientRequests <- request// 等待响应.fmt.Printf("answer: %d\n", <-request.resultChan)

On the server side, the function that processes the request is

funcchan *Request) {  forrange queue {    req.resultChan <- req.f(req.args)  }}

Obviously there is much more to be done to make this example more practical, but it is a framework for speed limiting, parallel, non-blocking RPC systems, and it does not see the use of mutexes (mutexes).

(v) Parallel

One application of parallel thinking is to use multi-core CPUs for parallel computing. If the calculation process can be divided into multiple fragments, it can be parallelized in such a way that the signal is sent over the channel after each fragment is completed.
Here the previous article has been introduced, see the Go Language concurrent programming (a)

(vi) Sync Tool sync. Waitgroup

Set a variable as the synchronization tool. This is one of the effective means to prevent the main goroutine from being prematurely run over. The code for declaring and initializing this variable is as follows:

var  waitgroup sync. Waitgroup //is used to wait for a set of operations to complete the synchronization tool.  waitgroup.add (3 ) //the number of operations in this group is 3.  numberChan1: = make  (chan  int64 ,  3 ) //digital channel 1.  numberChan2: = make  (chan  int64 ,  3 ) //digital channel 2.  numberChan3: = make  (chan  int64 ,  3 ) //digital channel 3  

Identifier sync. Waitgroup represents a type. This type of declaration exists in the code bundle Sync, and the type name is Waitgroup. In addition, the second statement above makes a "plus 3" operation, which means we are going to enable three goroutine later, or execute three go functions concurrently.

Let's look at the first go function: A number filter function that filters out numbers that cannot be divisible by 2.

gofunc// 数字过滤函数1。  forrange// 不断的从数字通道1中接收数字,直到该通道关闭。    if n%2 == 0// 仅当数字可以被2整除,才将其发送到数字通道2.      numberChan2 <- n    else {      fmt.Printf("Filter %d. [filter 1]\n", n)    }  }   close// 关闭数字通道2。  waitGroup.Done()   // 表示此操作完成。进行相应的“减1”}()

The number filter function 2 code is similar to the above, filtering out numbers that cannot be divisible by 5. As follows:

go   Func  () {//numeric filter function 2.  for  N: = range  numberChan2 {//continuously receives a number from digital channel 2 until the channel is closed.  if  n%5  == 0
     {//is sent to the digital channel 3 only if the number can be divisible by 5.  NUMBERCHAN3 <-N} else  {fmt. Printf ( "filter%d. [Filter 1]\n" , N)}} close  (n UMBERCHAN3) //off digital channel 3.  waitgroup.done () //indicates that this operation is complete. Make the corresponding "Minus 1" } ()  

As a result, the digital filter functions 1 and 2 are concatenated through the digital channel 2. Note that you should not forget to add a close operation to the digital channel NUMBERCHAN3 after the for statement in the number filter function 2, and to invoke the done method of the Waitgroup variable.

gofunc// 数字输出函数。  forrange// 不断的从数字通道3中接收数字,直到该通道关闭。    // 打印数字。  }  // 表示此操作完成。并“减1”。}()

The process of filtering the numbers is then activated. The specific activation method is to send a number to the digital channel NumberChan1. Add the following code after the above code:

for0100; i++ { // 先后向数字通道1传送100个范围在[0,100)的随机数。  numberChan1 <- rand.Int63n(100)}close(numberChan1) // 数字发送完毕,关闭数字通道1。对通道的关闭并不会影响到对已存于其中的数字的接收操作。

To enable this process to be fully implemented, we also need to add a statement at the end:

// 等待前面那组操作(共3个)的完成。

The call to the Waitgroup wait method is blocked until the three waitgroup.done () statements (i.e. three "minus 1 operations") in the previous three go functions are executed. That is, when the number of Waitgroup is reduced from 3 to 0 o'clock, the execution of the waitgroup.wait () statement can be resumed from the blocking and completed.

Reference article: Concurrent programming of Go language learning notes
Go concurrency programming Go language overview

Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.

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.