GO基础进阶篇 (九)、临界资源安全问题(锁、channel)
临界资源安全问题
在并发编程中对临界资源的处理不当,往往会导致数据的不一致问题
package mainimport ("fmt""time"
)func main() {a := 1go func() {a = 2fmt.Println("goroutine", a)}()a = 3fmt.Println("a", a)time.Sleep(time.Second * 3)fmt.Println("a1", a)//结果//a 3//goroutine 2//a1 2
}
1.售票问题
火车票售票程序。共有10张票,4个售票口同时出售,如何确保库存正常
package mainimport ("fmt""time"
)var ticket int = 10func main() {go sale("售票口1")go sale("售票口2")go sale("售票口3")go sale("售票口4")time.Sleep(time.Second * 3)//售票口2 当前剩余: 10//售票口3 当前剩余: 9//售票口4 当前剩余: 8//售票口1 当前剩余: 10//售票口4 当前剩余: 6//售票口1 当前剩余: 6//售票口2 当前剩余: 4//售票口3 当前剩余: 4//售票口2 当前剩余: 2//售票口3 当前剩余: 2//卖光了//售票口1 当前剩余: 2//卖光了//售票口4 当前剩余: 2//卖光了//售票口2 当前剩余: -2//卖光了
}func sale(name string) {for {if ticket > 0 {time.Sleep(time.Millisecond * 500)fmt.Println(name, "当前剩余:", ticket)ticket--} else {fmt.Println("卖光了")break}}
}
多个线程争抢时会出现问题。
2.mutex锁
sync 包提供了对互斥锁(Mutex)的支持,用于实现多个 goroutines 之间的互斥访问。互斥锁是一种同步原语,可以确保在任何时刻,只有一个 goroutine 能够访问共享资源。
package mainimport ("fmt""sync""time"
)var ticket int = 10
var mutex = sync.Mutex{}func main() {go sale("售票口1")go sale("售票口2")go sale("售票口3")go sale("售票口4")time.Sleep(time.Second * 8)//售票口1 当前剩余: 10//售票口1 当前剩余: 9//售票口4 当前剩余: 8//售票口2 当前剩余: 7//售票口3 当前剩余: 6//售票口1 当前剩余: 5//售票口4 当前剩余: 4//售票口2 当前剩余: 3//售票口3 当前剩余: 2//售票口1 当前剩余: 1//卖光了//卖光了//卖光了//卖光了
}func sale(name string) {for {mutex.Lock()if ticket > 0 {time.Sleep(time.Millisecond * 500)fmt.Println(name, "当前剩余:", ticket)ticket--} else {mutex.Unlock()fmt.Println("卖光了")break}mutex.Unlock()}
}
但是实际上,在GO语言的并发编程中,有一句经单的话:不要以共享内存的方式去通信,而要以通信的方式去共享内存。
在GO语言中,并不鼓励用锁的机制来保护共享状态,在不同的Goroutine中分享信息(以共享内存来通信)。而是鼓励通过channel将共享状态或共享状态的变化在各个Goroutine中之间传递(以通信的方式共享内存)。这样同样能像锁一样,保证同一时间只有一个Goroutine能访问共享状态。
3. WaitGroup
在上面的例子中,我们通过time.sleep()来让主线程等待。这个时间我们不能精准控制。而sync.WaitGroup(通常缩写为 wg)是一种用于等待一组 goroutines 完成执行的同步原语。WaitGroup 通过一个计数器来实现等待,计数器的初始值为 0。每当启动一个新的 goroutine 时,计数器就会递增。当 goroutine 完成时,就会调用 Done 方法将计数器递减。主程序可以调用 Wait 方法来阻塞,直到计数器减至零,表示所有的 goroutines 都已经执行完成。
package mainimport ("fmt""sync""time"
)var ticket int = 10
var mutex = sync.Mutex{}
var wg sync.WaitGroupfunc main() {wg.Add(4)go sale("售票口1")go sale("售票口2")go sale("售票口3")go sale("售票口4")wg.Wait()//售票口1 当前剩余: 10//售票口1 当前剩余: 9//售票口4 当前剩余: 8//售票口2 当前剩余: 7//售票口3 当前剩余: 6//售票口1 当前剩余: 5//售票口4 当前剩余: 4//售票口2 当前剩余: 3//售票口3 当前剩余: 2//售票口1 当前剩余: 1//卖光了//卖光了//卖光了//卖光了
}func sale(name string) {for {mutex.Lock()if ticket > 0 {time.Sleep(time.Millisecond * 500)fmt.Println(name, "当前剩余:", ticket)ticket--} else {mutex.Unlock()wg.Done()fmt.Println("卖光了")break}mutex.Unlock()}
}
4.channel
通道(Channel)是用于在 goroutines 之间进行通信的一种机制。通道提供了一种安全的数据传输方式,确保数据在发送和接收的过程中不会被竞争条件破坏。通道的主要目的是协调不同 goroutines 之间的执行。
package mainimport "fmt"func main() {var ch chan boolch = make(chan bool)go func() {for i := 0; i < 10; i++ {fmt.Println(i)}ch <- true}()data := <-chfmt.Println("通道里的值", data)
}
一个通道发送和接收数据,默认是阻塞的。当一个数据被发送到通道时,在发送语句中被阻塞,直到另一个Goroutine从该通道读取数据。
相对地,当从通道读取数据时,读取被阻塞,直到一个Goroutine将数据写入该通道。本身channel就是同步的,意味着同一时间,只能有一条goroutine来操作。
最后:通道是goroutine之间的连接,所以通道的发送和接收必须处在不同的goroutine中。
这些通道的特性是帮助Goroutines有效地进行通信,而无需像使用其他编程语言中非常常见的显式锁或条件变量。
死锁
如果创建了chan,没有Goroutine来使用了,则会出现死锁。
使用通道时要考虑的一一个重要因素是死锁。如果Goroutine在一个通道上发送数据,那么预计其他的Goroutine应该接收数据。如果这种情况不发生,那么程序将在运行时出现死锁。
类似地,如果Goroutine正在等待从通道接收数据,那么另一些Goroutine将会在该通道上写入数据,否则程序将会死锁。
售票问题的channel实现写法
package mainimport ("fmt""sync""time"
)var ticket int = 10
var wg sync.WaitGroupfunc main() {ticketCh := make(chan int)wg.Add(4)go sale("售票口1", ticketCh)go sale("售票口2", ticketCh)go sale("售票口3", ticketCh)go sale("售票口4", ticketCh)for {time.Sleep(time.Millisecond * 500)ticketCh <- ticketif ticket == 0 {break}}close(ticketCh)fmt.Println(ticket) //0wg.Wait()fmt.Println(ticket) //0
}func sale(name string, ticketCh chan int) {for {v, _ := <-ticketChfmt.Println(name, "当前剩余:", v)if v == 0 {wg.Done()break}ticket--}
}相关文章:
GO基础进阶篇 (九)、临界资源安全问题(锁、channel)
临界资源安全问题 在并发编程中对临界资源的处理不当,往往会导致数据的不一致问题 package mainimport ("fmt""time" )func main() {a : 1go func() {a 2fmt.Println("goroutine", a)}()a 3fmt.Println("a", a)time.Sl…...
Python基础-04(比较运算符、逻辑运算符)
文章目录 前言一、比较运算符二、逻辑运算符1.and(与)2.or(或)3.not(非)4.逻辑运算符的细节(短路原则)(着重理解) 总结 前言 1、比较运算符内容很简单&#…...
MySQL 四种插入命令及其特点与锁机制
目录 1. INSERT INTO 2. INSERT IGNORE INTO 3. INSERT INTO ... ON DUPLICATE KEY UPDATE 4. REPLACE INTO 总结 MySQL提供了多种数据插入方式,每种方式在处理唯一键冲突时的行为不同,同时也涉及不同的锁机制。 1. INSERT INTO INSERT INTO是标准…...
AKShare学习笔记
AKShare学习笔记 本文内容参考AKShare文档。AKShare开源财经数据接口库采集的数据都来自公开的数据源,数据接口查询出来的数据具有滞后性。接口参考AKShare数据字典。 AKShare环境配置 安装Anaconda,使用Anaconda3-2019.07版本包,配置清华数…...
A星寻路算法
A星寻路算法简介 A星寻路算法(A* Search Algorithm)是一种启发式搜索算法,它在图形平面上进行搜索,寻找从起始点到终点的最短路径。A星算法结合了广度优先搜索(BFS)和最佳优先搜索(Best-First S…...
QDialog
属性方法 样式表 background-color: qlineargradient(spread:reflect, x1:0.999896, y1:0.494136, x2:1, y2:1, stop:0 rgba(0, 0, 0, 255), stop:1 rgba(255, 255, 255, 255));border: 1px groove rgb(232, 232, 232);border-radius: 20px; QDialog 的常用方法: e…...
Spark中使用DataFrame进行数据转换和操作
Apache Spark是一个强大的分布式计算框架,其中DataFrame是一个核心概念,用于处理结构化数据。DataFrame提供了丰富的数据转换和操作功能,使数据处理变得更加容易和高效。本文将深入探讨Spark中如何使用DataFrame进行数据转换和操作࿰…...
windows11新装机,简单评测系统自带软件(基本涵盖日常所需应用)
新年将近,由于当年安排的失误,系统盘(100G)和照片视频盘(4T)容量不够了,大容量的那块机械盘放在机箱里就在耳朵根吵吵,烦得很,于是狠狠心决定扩容后重配重装。 2023年最后…...
概念解析 | Shapley值及其在深度学习中的应用
注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:Shapley值及其在深度学习中的应用。 1 背景介绍 在机器学习和数据分析中,理解模型的预测是非常重要的。尤其是在深度学习黑盒模型中,我们往往难以直观地理解模型的预测行为。为…...
ajax的完整写法——success/error/complete+then/catch/done+设置请求头两种方法——基础积累
ajax的完整写法——success/error/completethen/catch/done设置请求头两种方法——基础积累 1.完整写法——success/error/complete1.1 GET/DELETE——query传参1.2 GET/DELETE——JSON对象传参1.3 PUT/POST——JSON对象传参 2.简化写法——then/catch/done2.1 GET/DELETE——q…...
《Linux详解:深入探讨计算机基础》
《Linux详解:深入探讨计算机基础》 引言: 在计算机科学领域,操作系统是一个至关重要的概念,而Linux作为一种开源的Unix-like操作系统,不仅在服务器领域广泛应用,也在嵌入式系统、超级计算机等多个领域发挥…...
HarmonyOS 实践之应用状态变量共享
平时在开发的过程中,我们会在应用中共享数据,在不同的页面间共享信息。虽然常用的共享信息,也可以通过不同页面中组件间信息共享的方式,但有时使用应用级别的状态管理会让开发工作变得简单。 根据不同的使用场景,ArkTS…...
ThreadLocal共享变量
一、ThreadLocal 我们知道多线程访问同一个共享变量时,会出现线程安全问题,为了保证线程安全开发者需要对共享变量的访问操作进行适当的同步操作,如加锁等同步操作。 除此之外,Java提供了ThreadLocal类,当一个共享变…...
前端crypto-js 库: MD5
文章目录 什么是crypto-js安装依赖MD5 什么是crypto-js github地址: https://github.com/brix/crypto-js cryptojs文档: https://cryptojs.gitbook.io/docs/#encoders CryptoJS (crypto.js) 为 JavaScript 提供了各种各样的加密算法。 CryptoJS是一个JavaScript加密算法库&a…...
2024新年快乐
2024-1-1 祝福大家和自己健康喜乐,升职加薪,新年快乐 页面加载事件load 我们页面加载事件的触发是等所有的资源加载完毕时触发该事件。和click一样是事件,但是触发时机是等资源加载(浏览器)完毕。这个事件我们可以将…...
OpenCV-Python(21):轮廓特征及周长、面积凸包检测和形状近似
2. 轮廓特征 轮廓特征是指由轮廓形状和结构衍生出来的一些特征参数。这些特征参数可以用于图像识别、目标检测和形状分析等应用中。常见的轮廓特征包括: 面积:轮廓所包围的区域的面积。周长:轮廓的周长,即轮廓线的长度。弧长&…...
连接progressql报错Cannot load JDBC driver class ‘org.postgresql.Driver‘,亲测有效!!!
Jmeter连接progressql报错Cannot load JDBC driver class ‘org.postgresql.Driver’ 1.到官方下载驱动注意:根据项目的JDK版本来下载对应的驱动Download | pgJDBC 2.将postgresql-42.2.27.jar复制到lib目录下面, 然后重新启动 连接driver信息如下&#…...
SQLAlchemy快速入门
安装依赖 pip install sqlalchemy pip install pymysql创建数据库和表 # 创建数据库 drop database if exists sqlalchemy_demo; create database sqlalchemy_demo character set utf8mb4; use sqlalchemy_demo;# 创建表 drop table if exists user; create table user (id …...
java 纯代码导出pdf合并单元格
java 纯代码导出pdf合并单元格 接上篇博客 java导出pdf(纯代码实现) 后有一部分猿友叫我提供一下源码,实际上我的源码已经贴在帖子上了,都是同样的步骤,只是加多一点设置就可以了。今天我再次上传一下相对情况比较完整…...
Linux自己的应用商店yum
💫Linux系统如何安装软件 在Linux系统中我们可以通过多种方式安装软件,常见方式有以下三种: 1.源代码安装 2.rpm包安装 3.使用yum软件包管理器安装 早期人们通过下载软件源代码,然后再经过交叉编译等一系列工作下…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...
uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
