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软件包管理器安装 早期人们通过下载软件源代码,然后再经过交叉编译等一系列工作下…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...

如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...

R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...