Goroutine+channel+waitgroup use

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

Introduced

Goroutine

Golang language is one of the most characteristic of the East is this goroutine, many times asked why others Golang the use of, Golang network performance can be so good, general will think of Goroutine, mention Goroutine. In Linux, the minimum dispatch unit for the kernel is thread, and multiple thread threads in the same process correspond to multiple thread entities in the kernel. So thread is the kernel level, and gorountine is a different from the thread concept, Gorountine is a user state, another way of saying that Ctrip, is a user-state of a scheduling granularity, each gorountine also has its own stack space, And is in the user's memory. Golang implementation of a code fragment of user state is implemented efficiently, which is very effective at present, and it brings great convenience to user programming.

Channel

Channel is another of the most distinctive golang, in C we are all using pipelines, documents, signals and so on as a means of communication between the threads, but in Golang is almost all exclusively use channel this stuff. The channel, or "Pipeline," is a type of data that can be passed into the channel, which can be used to get data from it. In Golang there are other ways to communicate between threads or Gorountine, but Golang's programming guidelines strongly suggest that the channel be used wherever communication is required, so the channel plus the Goroutine, Can be combined into a simple and powerful processing model, where n work goroutine will process the intermediate results or the final result into a channel, and another m work goroutine from the channel to take the data, and then further processing, by combining this process, Thus competent for a variety of complex business.

Waitgroup

Go provides sync packet and channel to resolve synchronization and communication. Sync. Waitgroup is waiting for a group of threads to end. It implements a similar task queue structure where you can add tasks to the queue, remove the task from the queue when the task is complete, and if the tasks in the queue are not all complete, the queue will trigger blocking to prevent the program from continuing to run. Waitgroup is required when the current thread of execution needs to be blocked, waiting for a set of Goroutine to execute before continuing with the current threads.
Sync. Waitgroup has only 3 methods, ADD (), Done (), Wait (). where done () is the alias of Add (-1). Simply put, add is adding or reducing the number of waiting goroutine; Done: equivalent to add (-1) Adding a count using Add (), do () minus a count; Wait: Execution blocking until all Waitgroup number becomes 0.

Usage Introduction

The most common way: as a signal transmission

In the following example, in a child goroutine, a period of action is required, and the main goroutine can do something else, and then wait for the child goroutine to complete. The receiver will block until there is data coming. If the channel is unbuffered, the sender blocks until the receiver pulls the data out. If the channel has a buffer, the sender blocks until the data is copied to the buffer, and if the buffer is full, the sender can recover from the blocking state only after the receiver has taken away the data.

c: = make (chan int)  //Allocate a channel.    Start a goroutine to do something and send a message to Channelgo func () {       doSomethingForAWhile1 ()        C <-1//after it's done  , send a message, Here is the message is actually a value, this value is not important, the following will not be used, the key is to have such an action   } ()    doSomethingForAWhile2 ()    <-c   //here will always block, Until there is a message sent in, here is waiting for sequencing to complete, multi-goroutine collaboration can be used

Production of true consumer mode: as Message queue

This is a classic usage, one or more producers, the consumer may also be one or more, the classic solution in C + + is to use the blocking queue, the producer directly to the queue to send data, consumers from the queue to consume data, when the producer encountered a queue full of blocking, the consumer encountered when the queue is empty and blocked. The solution in Golang is to use the channel: forward the request to a channel, then initialize multiple goroutine to read the content in the channel and process it. The simple code is as follows

Create a global Channelvar Task_channel = Make (chan net. Conn)

Then, start multiple goroutine for consumption

For I: = 0; I < 5; i + + {    go func () {for        {            Select {case            task: = <-Task_channel:                process (Task)}}}    () }

After the server receives the request, the task is passed into the channel:

For I: = 0; I < 5; i++ {go func () {for {select {case conn: = <-task_channel:handleconn (conn)}}}}}for {conn, err: = Listener. Accept () if err! = Nil {Continue}count + = 1task_chanel <-Conn}}

Handling the situation of channel full

However, there is also a problem with the above scenario: the channel initialization is not set length, and the default length of the channel is 0, that is, only one element can be written, then the write will be blocked, so when all processing the request Goroutine is processing the request, then there is a request to come, will be block. Therefore, you need to add a length when the channel is initialized:

var Task_channel = make (chan net. Conn,task_channel_len)

In this way, we set the Task_channel_len large enough that the request can receive Task_channel_len requests at the same time without fear of being blocked. However, this is still problematic: what if there are more than Task_channel_len requests at the same time? On the one hand, this should be considered as an architectural problem, can be achieved through the expansion of the module and other operations to solve. On the other hand, the module itself should also consider how to "gracefully downgrade". In this case, we should expect the module to be able to inform the caller in a timely manner, "I've reached the limit and can't handle the request." In fact, this problem can be easily implemented in Golang: if the channel send and receive operations are executed in the SELECT statement and the blocking occurs, the default statement executes immediately.

Select {Case Task_channel <-task:    //do somethingdefault:    //warnning!    Return to FMT. Errorf ("Task_channel is full!")}

Time-out processing of channel

Even for complex, time-consuming tasks, you must set a time-out. On the one hand, it is possible for the business to have a time limit (users must see the results in XX minutes), on the other hand, the module itself can not be consumed in the task has been unable to end, so that other requests can not be normal processing. Therefore, it is also necessary to increase the timeout mechanism for the processing process.
I generally set the timeout scheme by combining the "receive results returned after receiving a channel" with the previous reference, adding a select to the outer layer awaiting the return channel and passing time in it. After () to determine the timeout.

Select {Case conn: = <-task_channel:handleconn (conn) Case <-time. After (time. Second * 3):    //Processing Timeout}

Channel to pass Channel

As a native type of the go language, the channel can naturally be passed through the channel. Passing the channel through the channel can be a very simple and graceful solution to some practical problems. We can pass the request through the channel in the main gorountine to the working goroutine. Similarly, we can also return the processing results to the main goroutine by channel.
Main Goroutine:

Type Request struct {    args        []int    Resultchan  chan int}request: = &request{[]int{3, 4, 5}, make (chan int)}//Send requestclientrequests <-request//Wait for response.fmt.Printf ("Answer:%d\n", <-request.resultchan )

The Master Goroutine requests the request channel and waits for the result channel to be sent. When the child goroutine finishes processing, the result is written to the result channel.

Func handle (Queue Chan *request) {for    req: = range Queue {result: = do_something ()        Req.resultchan <-result
  }}

The use of Waitgroup

Here is one of the simplest uses, and also the usual way.

Package Mainimport (    "FMT"    "Sync") func main () {    var wg sync. Waitgroupi: = 0 for    ; i <; i++ {        WG. ADD (1)        go func (i int) {            fmt. Println ("Hello World Test", I)            WG. Done ()        } (i)    }    WG. Wait () fmt. Println ("Run Done:", I)}

Reference: Https://golang.org/doc/effective_go.html#channels

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.