Sync packet and channel mechanism in Go language

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

It is very simple to implement concurrency in Golang, just add the keyword "Go" before the function that needs concurrency, but how to handle the synchronization and communication between different goroutine in the Go concurrency mechanism, Golang provides the sync packet and channel mechanism to solve this problem.

The Sync package provides basic synchronization primitives such as mutexes. Types other than Once and Waitgroup are mostly used for the underlying library routines. More advanced synchronization operations are performed through channels and communications.

type Cond    func NewCond(l Locker) *Cond    func (c *Cond) Broadcast()    func (c *Cond) Signal()    func (c *Cond) Wait()type Lockertype Mutex    func (m *Mutex) Lock()    func (m *Mutex) Unlock()type Once    func (o *Once) Do(f func())type Pool    func (p *Pool) Get() interface{}    func (p *Pool) Put(x interface{})type RWMutex    func (rw *RWMutex) Lock()    func (rw *RWMutex) RLock()    func (rw *RWMutex) RLocker() Locker    func (rw *RWMutex) RUnlock()    func (rw *RWMutex) Unlock()type WaitGroup    func (wg *WaitGroup) Add(delta int)    func (wg *WaitGroup) Done()    func (wg *WaitGroup) Wait()

And the sync in Golang is through sync. Waitgroup to achieve. Waitgroup: It implements a queue-like structure that can continue to add tasks to the queue, remove them from the queue when the task is complete, and, if the tasks in the queue are not fully completed, block by using the wait () function to prevent the program from continuing until all queue tasks are complete.

Waitgroup a total of three methods: ADD (delta int), done (), Wait (). Add: Adding or reducing the number of wait goroutine done: equivalent to add ( -1) Wait: Execute block until all Waitgroup number becomes 0

Specific examples are as follows:

package main    import (      "fmt"      "sync"  )    var waitgroup sync.WaitGroup    func Afunction(shownum int) {      fmt.Println(shownum)      waitgroup.Done() //任务完成,将任务队列中的任务数量-1,其实.Done就是.Add(-1)  }    func main() {      for i := 0; i < 10; i++ {          waitgroup.Add(1) //每创建一个goroutine,就把任务队列中任务的数量+1          go Afunction(i)      }      waitgroup.Wait() //.Wait()这里会发生阻塞,直到队列中所有的任务结束就会解除阻塞  }  

Online Example: https://www.bytelang.com/o/s/c/6z7UkvezTJg=

Usage scenarios:

The program needs to be concurrent, need to create multiple goroutine, and must wait for these concurrency to complete before continuing the next program execution. The Waitgroup feature is that wait () can be used to block until all tasks in the queue are complete without having to sleep for a fixed time. However, the disadvantage is that you cannot specify a fixed number of goroutine.

Channel mechanism:

Relative sync. Waitgroup, the use of channel practice in Golang synchronization is much simpler. The channel itself can be blocked by <-data transfer, channel is a built-in basic type in Golang, there are only 4 ways for channel operation:

Create channel (via make () function, including no cache channel and cache channel);

Adding data to the channel (Channel<-data);

Reading data from the channel (Data<-channel);

Close channel (implemented by the close () function, cannot save data to the channel after closing, but can continue to read data from channel)

The channel is divided into buffer channel and unbuffered channel, and the two channel creation methods are as follows:

var ch = make (chan int)//unbuffered channel, equivalent to make (chan int, 0)

var ch = make (chan int,10)//buffered channel, buffer size is 5

In which the unbuffered channel is blocked in both read and write, while the buffer channel is stored in the channel when the data is not reached the total number of channel buffers, it can be kept to the inside until the cache is full before blocking. Due to the presence of blocking, special attention is paid to using the channel to prevent the creation of deadlocks. Examples are as follows:

No cache channel:

package main    import "fmt"    func Afuntion(ch chan int) {      fmt.Println("finish")      <-ch  }    func main() {      ch := make(chan int) //无缓冲的channel      go Afuntion(ch)      ch <- 1            // 输出结果:      // finish  

Online Example: https://www.bytelang.com/o/s/c/3cxH7Jko7YY=

Code Analysis: First create a non-buffered channel CH, and then execute Go afuntion (CH), at this time to execute <-ch, then afuntion this function will block, no longer continue to execute, until the main process ch<-1 to channel CH The afuntion is not unblocked by injecting data into the process.

Correct:

Code Analysis: For this program (only a single-core CPU running program) first create a non-buffered channel CH, and then encounter Go afuntion (CH), see that no CPU can be used to run the task, the task is noted, wait until there is a CPU, then run the task, and then execute CH <-1, at this time the main goroutine block, find whether there are other processes, find Afuntion (CH) This goroutine, then execute the goroutine content until <-ch from the main goroutine to obtain data 1, Remove the main goroutine block. (Note: This type of execution is limited to single-core CPUs)

If you specify multiple CPUs to run, first run the primary goroutine to create the unbuffered channel, and then see if there is an idle CPU that can run another goroutine, and if so, run the coprocessor afuntion (CH), for multicore CPUs, The order in which the main goroutine and the other goroutine is run is indeterminate.

package main    import "fmt"  import "runtime"  import "time"    func Afuntion(ch chan int) {      fmt.Println("finish")      <-ch  }    func main() {      runtime.GOMAXPROCS(runtime.NumCPU())      ch := make(chan int) //无缓冲的channel      go Afuntion(ch)      time.Sleep(time.Nanosecond * 1000)      fmt.Println("main goroutine")      ch <- 1  }  

Online Example: https://www.bytelang.com/o/s/c/9z_uWI5ZumA=

Operation Result:

Finishmain Goroutine

or main goroutine

Finish

The execution order of the primary goroutine and the other goroutine is indeterminate (for multicore CPUs)

package main    import "fmt"    func Afuntion(ch chan int) {      fmt.Println("finish")      <-ch  }    func main() {      ch := make(chan int) //无缓冲的channel      //只是把这两行的代码顺序对调一下      ch <- 1      go Afuntion(ch)        // 输出结果:      // 死锁,无结果  }  

Online Example: https://www.bytelang.com/o/s/c/sLL_Cto3k4E=

Code Analysis: First create a non-buffered channel, and then in the main process into channel CH through the ch<-1 command to write data, then the main process block, can not execute the following go afuntions (CH), nature will not be able to release the main process blocking state, The system deadlock

Summarize:
For non-cached channel, put the channel and the data from the channel to the outside of the two operations can not be placed in the same process to prevent the occurrence of deadlocks, and should first use go to open a process to operate the channel, at this time to block the go process, And then the channel in the main process of the opposite operation (and go to the channel to reverse the operation), to implement the Go Association threads unlocked. That is, the go process must be in front, unlocking the coprocessor at the back.

With Cache channel:
In the case of a cache channel, as long as the channel is not in the cache, the data can be deposited into the channel until the cache is full; As long as the channel cache is not 0, the data can be taken out of the channel until the channel cache becomes 0.

Thus, relative to the non-cache channel, with cache channel is not easy to cause deadlock, can be at the same time in a goroutine ease of use,

Close ():

Close is mainly used to close the channel channels, where it is close (channel), and where real producers are closing the channel, rather than closing it in the consumer's place. And when the channel is closed, it is no longer possible to continue to deposit data in the channel, but can continue to read from the channel. Examples are as follows:

package main    import "fmt"    func main() {      var ch = make(chan int, 20)      for i := 0; i < 10; i++ {          ch <- i      }      close(ch)      //ch <- 11 //panic: runtime error: send on closed channel      for i := range ch {          fmt.Println(i) //输出0 1 2 3 4 5 6 7 8 9      }  

Online Example: https://www.bytelang.com/o/s/c/XBiMiCoE7dc=

Channel Blocking timeout Processing:
Goroutine sometimes go into blocking situations, so how to avoid the blockage of the whole program due to channel blocking? Solution: Set timeout processing via Select, as follows:

package main     import (      "fmt"      "time"  )    func main() {      c := make(chan int)      o := make(chan bool)      go func() {          for {              select {              case i := <-c:                  fmt.Println(i)              case <-time.After(time.Duration(3) * time.Second):    //设置超时时间为3s,如果channel 3s钟没有响应,一直阻塞,则报告超时,进行超时处理.                  fmt.Println("timeout")                  o <- true                  break              }          }      }()      <-o  }

Online Example: https://www.bytelang.com/o/s/c/6V74LnkRLN0=

Golang Concurrency Summary:

Concurrency two ways: sync. Waitgroup, the best advantage of this approach is that wait () can block all tasks in the queue before it is unblocked, but its disadvantage is that the number of concurrent threads cannot be specified.
Channel Advantages: The ability to use a channel with a buffer to specify the concurrent Goroutine, more flexible. However, its disadvantage is that it is easy to create deadlocks if used improperly, and he also needs to decide whether or not the concurrent goroutine is executed.

However, the channel is more flexible, more convenient to use, and through the time-out processing mechanism can be very good to avoid the channel caused by the program deadlock, so the use of channel to achieve program concurrency, more convenient and easier to use.

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.