首页 > 其他分享 >Go sync并发工具包

Go sync并发工具包

时间:2023-02-04 19:11:08浏览次数:41  
标签:wg WaitGroup defer sync 工具包 mutex func Go

简介

在Java中提供Sychronized关键字提供独占锁,Lock类提供读写锁。在sync包中实现的功能也是与锁相关,包中主要包含的有:

  • sync.Map:并发安全 map
  • sync.Mutex:锁
  • sync.RWMutex:读写锁
  • sync.Once:只执行一次
  • sync.WaitGroup: goroutine 之间同步
  • sync.Pool:复用缓存池

sync.Map

key 和 value 类型都是 Any。意味着你要搞各 种类型断言

image-20230203142912299

使用示例

package main

import (
	"fmt"
	"sync"
)

func main() {
	m := sync.Map{}
	m.Store("cat", "Tom")
	m.Store("mouse", "Jerry")

	// 这里重新读取出来的,就是
	val, ok := m.Load("cat")
	if ok {
		fmt.Println(len(val.(string)))
		fmt.Printf("%s \n", val)
	}
}

image-20230204181456737

sync.Mutex 和sync.RWMutex

使用案例

package main

import (
	"sync"
)

var mutex sync.Mutex
var rwMutex sync.RWMutex

func Mutex() {
	mutex.Lock()
	defer mutex.Unlock()
	// 你的代码
}

func RwMutex() {
	// 加读锁
	rwMutex.RLock()
	defer rwMutex.RUnlock()

	// 也可以加写锁
	rwMutex.Lock()
	defer rwMutex.Unlock()
}

// 不可重入例子
func Failed1() {
	mutex.Lock()
	defer mutex.Unlock()

	// 这一句会死锁
	// 但是如果你只有一个goroutine,那么这一个会导致程序崩溃
	mutex.Lock()
	defer mutex.Unlock()
}

// 不可升级
func Failed2() {
	rwMutex.RLock()
	defer rwMutex.RUnlock()

	// 这一句会死锁
	// 但是如果你只有一个goroutine,那么这一个会导致程序崩溃
	mutex.Lock()
	defer mutex.Unlock()
}

mutex家族注意事项

  • 尽量用 RWMutext
  • 尽量用 defer 来释放锁,防止panic没有释放锁
  • 不可重入:lock 之后,即便是同一个线程(goroutine),也无法再次加锁(写递归函数要小心)
  • 不可升级:加了读锁之后,如果试图加写锁,锁不升级

不可重入和不可升级,和很多语言的实现都是不同 的,因此要小心使用

sync.Once

package main

import (
	"fmt"
	"sync"
)

func main() {
	PrintOnce()
	PrintOnce()
	PrintOnce()
}

var once sync.Once

// 这个方法,不管调用几次,只会输出一次
func PrintOnce() {
	once.Do(func() {
		fmt.Println("只输出一次")
	})
}

image-20230204182436334

sync.WaitGroup

WaitGroup,它有3个函数:

  • Add():在被等待的协程启动前加1,代表要等待1个协程。
  • Done():被等待的协程执行Done,代表该协程已经完成任务,通知等待协程。
  • Wait(): 等待其他协程的协程,使用Wait进行等待。
type WaitGroup
func (wg *WaitGroup) Add(delta int){}
func (wg *WaitGroup) Done(){}
func (wg *WaitGroup) Wait(){}

下怎么用WaitGroup实现上面的问题。

队长先创建一个WaitGroup对象wg,

每个队员都是1个协程, 队长让队员出发前,使用wg.Add(),

队员出发寻找钥匙,队长使用wg.Wait()等待(阻塞)所有队员完成,

某个队员完成时执行wg.Done(),等所有队员找到钥匙,

wg.Wait()则返回,完成了等待的过程,接下来就是开箱。

package main

import (
	"fmt"
	"sync"
)

func main() {
	// 钥匙
	key := 0
	// 资本家队长创建队伍
	wg := sync.WaitGroup{}
	// 这个队伍十个人
	wg.Add(10)
	for i := 1; i <= 10; i++ {
		go func(val int) {
			key = val
			// 打工人队员找到了钥匙
			wg.Done()
		}(i)
	}
	// 队长等待队伍集合,拿回钥匙,如果不等待,钥匙数量不定
	// 把这个注释掉你会发现,钥匙数量不知道会有多少个
	wg.Wait()
	fmt.Println(key)
}

sync.Pool

pool是什么

Golang在 1.3 版本的时候,在sync包中加入一个新特性:Pool。 简单的说:就是一个临时对象池

为什么需要sync.pool

保存和复用临时对象,减少内存分配,降低GC压力
对象越多GC越慢,因为Golang进行三色标记回收的时候,要标记的也越多,自然就慢了

使用案例

package main

import (
	"fmt"
	"sync"
)

func main() {

	// 初始化一个pool
	pool := sync.Pool{
		// 默认的返回值设置,不写这个参数,默认是nil
		New: func() interface{} {
			return &user{}
		}}

	// Get 返回的是 interface{},所以需要类型断言
    // Get 方法会返回 Pool 已经存在的对象;如果没有就使用New方法创建.
	u := pool.Get().(*user)
    // 对象或资源不用时,调用 Put 方法把对象或资源放回池子,
    // 池子里面的对象啥时候真正释放是由 go_runtime进行回收,是不受外部控制的
	// defer 还回去
	defer pool.Put(u)

	// 紧接着重置 u 这个对象
	u.Reset("Tom", "[email protected]")

	// 下边就是使用 u 来完成你的业务逻辑
	fmt.Printf("%+v", u)
}

type user struct {
	Name  string
	Email string
}

// 一般来说,复用对象都要求我们取出来之后,
// 重置里面的字段
func (u *user) Reset(name string, email string) {
	u.Email = email
	u.Name = name
}

image-20230204185807141

标签:wg,WaitGroup,defer,sync,工具包,mutex,func,Go
From: https://www.cnblogs.com/makalochen/p/17092165.html

相关文章

  • Django Ajax传值测试
    效果图如下:输入什么内容,点击提交按钮下面就添加新输入的数据,数据处理成JSON字符串传到后台,如果不用JSON,就是一个字符串,django目前暂用request.body对象取值。html文件:<!DOCT......
  • 【Django drf】视图类APIView之五层封装 ApiView的类属性 drf配置文件
    目录ApiView的类属性drf配置文件之查找顺序drf之请求APIView之请求相关配置drf之响应APIView之响应相关配置Response对象属性视图类序列化类路由基于GenericAPIview写五......
  • GO语言的实战学习(猜谜游戏和在线词典)| 青训营笔记
    一.GO语言的实战学习1.1前言在上文我们急速学习了Go语言的入门,今天我们来学习一下Go语言的实战二.猜谜游戏1.导入依赖包:"math/rand",代码如下:import("fmt""ma......
  • 在Python程序中操作MongoDB
    在Python程序中操作MongoDB可以通过pip安装pymongo来实现对MongoDB的操作。pipinstallpymongo进入Python交互式环境,就可以执行以下的操作。>>>frompymongoimpo......
  • Django内置权限系统源码解读
    前言之前有篇文章​​Django自定义认证系统原理及源码分析解读​​带大家分析解读了Django的认证逻辑,而且我们也知道认证是基础,认证通过之后,用户登录到系统,能看到那些,......
  • GO 并发
    简介Go语言支持并发,我们只需要通过go关键字来开启goroutine即可。goroutine是轻量级线程也有叫用户级线程,协程的,goroutine的调度是由Golang运行时进行......
  • async和await-cnblog
    在之前对promise对象的处理使用Promise原型函数then,catch解决回调地狱的问题,但存在冗余代码.then().then().then()asyncawaites8(esma2017)引入awaitPromis......
  • 论文《Proximal Policy Optimization Algorithms》即PPO算法的代码及解读
    代码https://github.com/openai/lm-human-preferences在train_policy.py文件看出有一个​​ref_policy​​作为ground-truth在train_reward.py文件看出可以同时用于​......
  • mongo 日常操作
    连接到mongo1或2:1#[email protected]#mongo127.0.0.1:27017/-uroot-puex@2021查看全局所有账户:useadmindb.auth('admi......
  • 通过一个示例形象地理解C# async await 非并行异步、并行异步、并行异步的并发量控制
    前言接上一篇通过一个示例形象地理解C#asyncawait异步我在.NET与大数据中吐槽前同事在双层循环体中(肯定是单线程了)频繁请求es,导致接口的总耗时很长。这不能怪前同......