广告
返回顶部
首页 > 资讯 > 后端开发 > GO >GO语言并发之好用的sync包详解
  • 437
分享到

GO语言并发之好用的sync包详解

GO语言并发 sync包GO语言 sync包GO sync包 2022-12-29 12:12:31 437人浏览 泡泡鱼
摘要

目录sync.Map 并发安全的Mapsync.Once 只执行一次sync.Cond 条件变量控制小结sync.Map 并发安全的Map 反例如下,两个Goroutine分别读写。

sync.Map 并发安全的Map

反例如下,两个Goroutine分别读写。

func unsafeMap(){
	var wg sync.WaitGroup
	m := make(map[int]int)
	wg.Add(2)
	go func() {
		defer wg.Done()
		for i := 0; i < 10000; i++ {
			m[i] = i
		}
	}()

	go func() {
		defer wg.Done()
		for i := 0; i < 10000; i++ {
			fmt.Println(m[i])
		}
	}()
	wg.Wait()
}

执行报错:

0
fatal error: concurrent map read and map write

goroutine 7 [running]:
runtime.throw({0x10a76fa, 0x0})
......

使用并发安全的Map

func safeMap() {
	var wg sync.WaitGroup
	var m sync.Map
	wg.Add(2)
	go func() {
		defer wg.Done()
		for i := 0; i < 10000; i++ {
			m.Store(i, i)
		}
	}()

	go func() {
		defer wg.Done()
		for i := 0; i < 10000; i++ {
			fmt.Println(m.Load(i))
		}
	}()
	wg.Wait()
}
  • 不需要make就能使用
  • 还内置了StoreLoadLoadOrStoreDeleteRange等操作方法,自行体验。

sync.Once 只执行一次

很多场景下我们需要确保某些操作在高并发的场景下只执行一次,例如只加载一次配置文件、只关闭一次通道等。

init 函数是当所在的 package 首次被加载时执行,若迟迟未被使用,则既浪费了内存,又延长了程序加载时间。

sync.Once 可以在代码的任意位置初始化和调用,因此可以延迟到使用时再执行,并发场景下是线程安全的。

在多数情况下,sync.Once 被用于控制变量的初始化,这个变量的读写满足如下三个条件:

  • 当且仅当第一次访问某个变量时,进行初始化(写);
  • 变量初始化过程中,所有读都被阻塞,直到初始化完成;
  • 变量仅初始化一次,初始化完成后驻留在内存里。
var loadOnce sync.Once
var x int
for i:=0;i<10;i++{
    loadOnce.Do(func() {
        x++
    })
}
fmt.Println(x)

输出

1

sync.Cond 条件变量控制

sync.Cond 基于互斥/读写锁,它和互斥锁的区别是什么呢?

互斥锁 sync.Mutex 通常用来保护临界区和共享资源,条件变量 sync.Cond 用来协调想要访问共享资源的 goroutine

也就是在存在共享变量时,可以直接使用sync.Cond来协调共享变量,比如最常见的共享队列,多消费多生产的模式。

我一开始也很疑惑为什么不使用channelselect的模式来做生产者消费者模型(实际上也可以),这一节不是重点就不展开讨论了。

创建实例

func NewCond(l Locker) *Cond

NewCond 创建 Cond 实例时,需要关联一个锁。

广播唤醒所有

func (c *Cond) Broadcast()

Broadcast 唤醒所有等待条件变量 cgoroutine,无需锁保护。

唤醒一个协程

func (c *Cond) Signal()

Signal 只唤醒任意 1 个等待条件变量 cgoroutine,无需锁保护。

等待

func (c *Cond) Wait()

每个 Cond 实例都会关联一个锁 L(互斥锁 *Mutex,或读写锁 *RWMutex),当修改条件或者调用 Wait 方法时,必须加锁。

举个不恰当的例子,实现一个经典的生产者和消费者模式,但有先决条件:

  • 边生产边消费,可以多生产多消费。
  • 生产后通知消费。
  • 队列为空时,暂停等待。
  • 支持关闭,关闭后等待消费结束。
  • 关闭后依然可以生产,但无法消费了。
var (
	cnt          int
	shuttingDown = false
	cond         = sync.NewCond(&sync.Mutex{})
)
  • cnt 为队列,这里直接用变量代替了,变量就是队列长度。
  • shuttingDown 消费关闭状态。
  • cond 现成的队列控制。

生产者

func Add(entry int) {
	cond.L.Lock()
	defer cond.L.Unlock()
	cnt += entry
	fmt.Println("生产咯,来消费吧")
	cond.Signal()
}

消费者

func Get() (int, bool) {
	cond.L.Lock()
	defer cond.L.Unlock()
	for cnt == 0 && !shuttingDown {
		fmt.Println("未关闭但空了,等待生产")
		cond.Wait()
	}
	if cnt == 0 {
		fmt.Println("关闭咯,也消费完咯")
		return 0, true
	}
	cnt--
	return 1, false
}

关闭程序

func Shutdown() {
	cond.L.Lock()
	defer cond.L.Unlock()
	shuttingDown = true
	fmt.Println("要关闭咯,大家快消费")
	cond.Broadcast()
}

主程序

var wg sync.WaitGroup
	wg.Add(2)
	time.Sleep(time.Second)
	go func() {
		defer wg.Done()
		for i := 0; i < 10; i++ {
			go Add(1)
			if i%5 == 0 {
				time.Sleep(time.Second)
			}
		}
	}()
	go func() {
		defer wg.Done()
		shuttingDown := false
		for !shuttingDown {
			var cur int
			cur, shuttingDown = Get()
			fmt.Printf("当前消费 %d, 队列剩余 %d \n", cur, cnt)
		}
	}()
	time.Sleep(time.Second * 5)
	Shutdown()
	wg.Wait()
  • 分别创建生产者与消费者。
  • 生产10个,每5个休息1秒。
  • 持续消费。
  • 主程序关闭队列。

输出

生产咯,来消费吧
当前消费 1, 队列剩余 0 
未关闭但空了,等待生产
生产咯,来消费吧
生产咯,来消费吧
当前消费 1, 队列剩余 1 
当前消费 1, 队列剩余 0 
未关闭但空了,等待生产
生产咯,来消费吧
生产咯,来消费吧
生产咯,来消费吧
当前消费 1, 队列剩余 2 
当前消费 1, 队列剩余 1 
当前消费 1, 队列剩余 0 
未关闭但空了,等待生产
生产咯,来消费吧
生产咯,来消费吧
生产咯,来消费吧
生产咯,来消费吧
当前消费 1, 队列剩余 1 
当前消费 1, 队列剩余 2 
当前消费 1, 队列剩余 1 
当前消费 1, 队列剩余 0 
未关闭但空了,等待生产
要关闭咯,大家快消费
关闭咯,也消费完咯
当前消费 0, 队列剩余 0

小结

1.sync.Map 并发安全的Map。

2.sync.Once 只执行一次,适用于配置读取、通道关闭。

3.sync.Cond 控制协调共享资源。

以上就是GO语言并发之好用的sync包详解的详细内容,更多关于GO语言 sync包的资料请关注编程网其它相关文章!

您可能感兴趣的文档:

--结束END--

本文标题: GO语言并发之好用的sync包详解

本文链接: https://www.lsjlt.com/news/176165.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • GO语言并发之好用的sync包详解
    目录sync.Map 并发安全的Mapsync.Once 只执行一次sync.Cond 条件变量控制小结sync.Map 并发安全的Map 反例如下,两个Goroutine分别读写。...
    99+
    2022-12-29
    GO语言 并发 sync包 GO语言 sync包 GO sync包
  • 详解go语言的并发
    目录1、启动go语言的协程2、runtime.Goexit()方法。立即终止当前的协程3、runtime.GOMAXPROCS()表示go使用几个cpu执行代码4、管道定义和创...
    99+
    2022-06-07
    详解go语言 GO 并发 go语言
  • GO中sync包自由控制并发示例详解
    目录资源竞争sync.Mutexsync.RWMutexsync.WaitGroupsync.Oncesync.Cond资源竞争 channel 常用于并发通信,要保证并发安全,主要...
    99+
    2022-11-11
  • Go语言并发之原子操作详解
    目录修改赋值与读取比较并交换小结代码中的加锁操作因为涉及内核态的上下文切换会比较耗时、代价比较高。针对基本数据类型我们还可以使用原子操作来保证并发安全,因为原子操作是Go语言提供的方...
    99+
    2022-12-29
    Go语言 并发 原子操作 Go语言 原子操作 Go语言 并发
  • Go语言基础之Time包详解
    Time包是Go语言中用于处理时间的一个标准库。它提供了一系列函数和类型,用于获取当前时间、时间格式化、时间计算等操作。在Go语言中...
    99+
    2023-08-29
    Go语言
  • Go语言学习之context包的用法详解
    目录前言需求一需求二Context 接口emptyCtxvalueCtx类型定义WithValuecancelCtx类型定义cancelCtxWithCanceltimerCtx类型...
    99+
    2022-11-11
  • Go语言os包用法详解
    目录os 包中的常用函数os/exec 执行外部命令os/user 获取当前用户信息os/signal 信号处理Go语言的 os 包中提供了操作系统函数的接口,是一个比较重要的包。顾...
    99+
    2023-05-14
    Go语言os包 Go os包使用
  • Go语言中的闭包详解
    一、函数的变量作用域和可见性 1.全局变量在main函数执行之前初始化,全局可见 2.局部变量在函数内部或者if、for等语句块有效,使用之后外部不可见 3.全局变量和局部变量同名的...
    99+
    2022-11-13
  • Go语言中的包Package详解
    问题一、Go使用Package组织源码的好处是什么? 1.任何源码属于一个包 2.用包组织便于代码的易读和复用 问题二、Go语言中Package的种类 Go语言中存在两种包、一种是可...
    99+
    2022-11-13
  • Go语言中map使用和并发安全详解
    目录1 map使用1.1 map定义1.2 map的使用和概念1.3 map的容量1.4 map的使用1.4.1 map的遍历1.4.2 map的删除和断言1.5 map的坑2 并发...
    99+
    2022-11-13
  • GO语言中通道和sync包的使用教程分享
    目录GO通道和 sync 包的分享通道是什么通道能做什么通道有哪几种无缓冲通道有缓冲的通道单向通道如何创建和声明一个通道声明通道初始化通道如何操作 channel通道异常情况梳理每一...
    99+
    2023-02-24
    GO 通道使用 GO sync包使用 GO 通道 GO sync包
  • 详解Go语言微服务开发框架之Go chassis
    目录引言架构获取配置配置项形态配置运行时热加载例子引言 https://github.com/go-chassis/go-chassis是一个微服务开发框架,而微服务开发框架带来的其...
    99+
    2022-11-12
  • GO语言的控制语句详解包括GO语言的指针语法
    GO语言的控制语句 判断结构:if-else 和大多数编程语言一样,if-else的用法基本都一样,直接来一个GO语言的例子 package main import ( "f...
    99+
    2022-12-19
    GO语言的控制语句详解 GO语言指针语法 GO语言switch结构 GO语言for结构
  • Go语言开发保证并发安全实例详解
    目录什么是并发安全?Mutex悲观锁乐观锁版本号机制CAS互斥锁读写互斥锁什么是并发安全? 在高并发场景下,进程、线程(协程)可能会发生资源竞争,导致数据脏读、脏写、死锁等问题,为了...
    99+
    2022-11-11
  • Go语言使用goroutine及通道实现并发详解
    目录使用通道接收数据阻塞接收数据非阻塞接收数据接收任意数据,忽略掉接收的数据循环接收数据使用通道接收数据 在上一篇文章中介绍了通道以及使用通道发送数据,本篇接着了解通道的基本内容,如...
    99+
    2022-11-11
  • Go语言中的并发编程:同步机制详解
    在Go语言中,支持并发编程是其一个非常重要的特性。而并发编程中的同步机制也是非常重要的,它能够确保程序的正确性和稳定性。本文将详细介绍Go语言中的同步机制,并通过演示代码来加深理解。 互斥锁(Mutex) 互斥锁是Go语言中最基础的同...
    99+
    2023-08-23
    并发 同步 索引
  • 详解go语言中并发安全和锁问题
    首先可以先看看这篇文章,对锁有些了解 GO语言并发编程之互斥锁、读写锁详解 Mutex-互斥锁 Mutex 的实现主要借助了 CAS 指令 + 自旋 + 信号量 数据结构: ty...
    99+
    2022-11-12
  • Go语言学习之WaitGroup用法详解
    目录前言小试牛刀总览底层实现结构体AddDoneWait易错点总结前言 在前面的文章中,我们使用过 WaitGroup 进行任务编排,Go语言中的 ...
    99+
    2022-06-11
    GO 学习 go语言
  • Go语言学习之指针的用法详解
    目录引言一、定义结构体1. 语法格式2. 示例二、访问结构体成员三、结构体作为函数参数四、结构体指针总结引言 Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义...
    99+
    2022-11-13
  • Go语言学习之链表的使用详解
    目录1. 什么是链表2. 单项链表的基本操作3. 使用 struct 定义单链表4. 尾部添加节点5. 头部插入节点6. 指定节点后添加新节点7. 删除节点1. 什么是链表 链表是一...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作