Golang 并发&同步的详细原理和使用技巧

文章目录

  • Golang 并发&同步的详细原理和使用技巧
    • Golang 并发概要说明
      • 并发模型
      • go 关键字实现并发
    • Golang 并发&同步的设计和实现
      • 为何需要同步控制
      • 并发和 context
      • 并发 和 sync(锁)
        • sync.Mutex 互斥锁
        • sync.RWMutex 读写锁
      • 并发和 Channel
      • 并发 和 sync(sync.WaitGroup)
        • WaitGroup 的介绍
        • WaitGroup vs Channel
        • WaitGroup 的使用示例
        • WaitGroup 的问题
      • 并发 和 errgroup
        • 官方原生的 errgroup
        • 其他扩展的 errgroup
    • 最后

Golang 并发&同步的详细原理和使用技巧

Golang 并发概要说明

并发模型

Golang 的并发模型属于一种很典型的 CSP(communicating sequential processes) 并发模型,其核心是不要通过共享内存来通信,而应该通过通信来共享内存。具体实现,就是通过 goroutine 来实现并发,然后并发的 goroutine 之间通过 Channel 来进行通信; 为此,Golang 的并发也有两个明显特点:

  1. Golang 非常善于并发,可以很简单的来实现(goroutine),直接一个 go 语句基就搞定,通过提高并发可以显著提高处理效率。
  2. Golang 的 goroutine 并发协程之间一般是采用通信的方式来共享变量或者逻辑交互,而这其中就可以结合 channel 来让并发协程之间进行共享数据,而 channel 就是各个 goroutine 之间通信的管道。

go 关键字实现并发

Golang 并发是通过 goroutine 协程来实现的,通过 go 关键字可以非常简单的启动一个协程;通过 go 关键字启动协程之后, 主进程并不会等待协程的执行,而是继续执行直至结束。因此,如果父子进程之间要有控制关系的话,就需要同步机制来保证。

Golang 并发&同步的设计和实现

为何需要同步控制

Go 既然天然支持并发,并且可以很简单的实现并发编程,那么这些并发的协程之间,如果同时访问访问内存中的同一个数据,在没有同步的机制下,那么同一个数据的访问一定会出现错乱,因此,在并发的场景,一定要通过同步机制才能确保同一内存数据的正确访问。

并发和 context

当需要进行多批次的计算任务同步,或者需要一对多的协作流程的时候;通过 Context 的关联关系(go 的 context 被设计为包含了父子关系),我们就可以控制子协程的生命周期,而其他的同步方式是无法控制其生命周期的,只能是被动阻塞等待完成或者结束。context 控制子协程的生命周期,是通过 context 的 context.WithTimeout 机制来实现的,这个是一般系统中或者底层各种框架、库的普适用法。

context 对并发做一些控制包括 Context Done 取消、截止时间取消 context.WithDeadline、超时取消 context.WithTimeout 等。

一个简单的 context.WithTimeout 示例如下:

	package main
	
	import (
		"fmt"
		"sync"
		"time"
	
		"golang.org/x/net/context"
	)
	
	var (
		wg sync.WaitGroup
	)
	
	func work(ctx context.Context) error {
		defer wg.Done()
	
		for i := 0; i < 1000; i++ {
			select {
			case <-time.After(2 * time.Second):
				fmt.Println("Do work ", i)
	
			// we received the signal of cancelation in this channel
			case <-ctx.Done():
				fmt.Println("Cancel th

你可能感兴趣的:(后端,Golang,服务器,golang,后端,开发语言)