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软件包管理器安装 早期人们通过下载软件源代码,然后再经过交叉编译等一系列工作下…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...
RLHF vs RLVR:对齐学习中的两种强化方式详解
在语言模型对齐(alignment)中,强化学习(RL)是一种重要的策略。而其中两种典型形式——RLHF(Reinforcement Learning with Human Feedback) 与 RLVR(Reinforcement Learning with Ver…...
