湖北广盛建设集团网站/网站seo网络优化
线程安全和锁
一、全局解释器锁
首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。
GIL全称global interpreter lock,全局解释器锁。
每个线程在执行的时候都需要先获取GIL,保证同一时刻只有一个线程可以执行代码,即同一时刻只有一个线程使用CPU。在CPython中,每一个Python线程执行前都需要去获得GIL锁 ,获得该锁的线程才可以执行,没有获得的只能等待 ,当具有GIL锁的线程运行完成后,其他等待的线程就会去争夺GIL锁,这就造成了,在Python中使用多线程,但同一时刻下依旧只有一个线程在运行 ,所以Python多线程其实并不是「并行」的,而是「并发」 。
看到下图,图中是Python中GIL的工作实例,其中有3个线程,线程与线程之间是顺序执行的 ,每个线程开始执行时都会去获得GIL,防止其他线程线程运行 ,每执行完一段时间后,就会释放GIL,让别的线程可以去争夺执行权限,如果自己本身也没有执行完,则本身也会参与这次争夺 。
# 多线程的代码
import threading, timedef add(n):sum = 0while sum < n:sum += 1print(f'sum:{sum}')if __name__ == '__main__':start = time.time()n = 500000000t1 = threading.Thread(target=add, args=[n // 2])t2 = threading.Thread(target=add, args=[n // 2])t1.start()t2.start()t1.join()t2.join()print('run time: %s' % str(time.time() - start))
# 单线程的代码
import timedef add(n):sum = 0while sum < n:sum += 1print(f'sum:{sum}')if __name__ == '__main__':start = time.time()add(500000000)print('run time: %s' % str(time.time() - start))
总结
-
GIL解决方法:
-
使用其他语言写的python解释器(不推荐,还是用官方CPython好)
eg:Jython(java);IronPython(.net);pypy(Python) -
不使用多线程,使用多进程-进程里加协程实现多任务来充分利用多核CPU (推荐)
-
即使存在GIL 在有IO等待操作的程序中,还是多线程快,当然没有资源等待的还是单线程快(科学计算,累加等等)
但需要注意的是线程有了GIL后并不意味着使用Python多线程时不需要考虑线程安全 ,「GIL的存在是为了方便使用C语言编写CPython解释器的编写者,而顶层使用Python时依旧要考虑线程安全」 。
二、线程安全
当多个线程同时访问一个对象时,不管如何计算,如果调用这个对象的行为都可以获得正确的结果,那就称这个对象时线程安全的。 如果出现了“脏数据”。则线程不安全。
脏数据 :产生脏数据的原因是,当一个线程在对数据进行修改时,修改到一半时另一个线程读取了未经修改的数据并进行修改。如何避免脏数据的产生呢?一个办法就是用join方法,即先让一个线程执行完毕再执行另一个线程。但这样的本质是把多线程变成了单线程,失去了多线程的意义。另一个办法就是用线程锁。
import threadingg_number = 0def hello():global g_numberfor i in range(1000000): # 加的次数越大越容易出现资源竞争问题g_number += 1print(f'thd1运行的结果为:{g_number}')def world():global g_numberfor i in range(1000000):g_number += 1print(f'thd2运行的结果为:{g_number}')if __name__ == '__main__':thd1 = threading.Thread(target=hello)thd2 = threading.Thread(target=world)thd1.start()thd2.start()# 阻塞等待thd1.join()thd2.join()print(g_number) # 结果随机 可能小于等于2000000
三、锁
锁是Python提供给我们能够自行操控线程切换的一种手段,使用锁可以让线程的切换变的有序。
一旦线程的切换变的有序后,各个线程之间对数据的访问、修改就变的可控,所以若要保证线程安全,就必须使用锁。
threading模块中提供了5种最常见的锁,下面是按照功能进行划分:
- 同步锁:lock(一次只能放行一个)
- 递归锁:rlock(一次只能放行一个)
- 条件锁:condition(一次可以放行任意个)
- 事件锁:event(一次全部放行)
- 信号量锁:semaphore(一次可以放行特定个)
1、同步锁
同一时刻的一个进程下的一个线程只能使用一个cpu,要确保这个线程下的程序在一段时间内被cpu执,那么就要用到同步锁。只需要在对公共数据的操作前后加上上锁和释放锁的操作即可。
死锁: 指两个或两个以上的线程或进程在执行程序的过程中,因争夺资源而相互等待的一个现象。
import threadingg_number = 0
lock = threading.Lock()def hello():global g_numberfor i in range(1000000): # 加的次数越大越容易出现资源竞争问题with lock:g_number += 1print(f'thd1运行的结果为:{g_number}')def world():global g_numberfor i in range(1000000):with lock:g_number += 1print(f'thd2运行的结果为:{g_number}')if __name__ == '__main__':thd1 = threading.Thread(target=hello)thd2 = threading.Thread(target=world)thd1.start()thd2.start()# 阻塞等待thd1.join()thd2.join()print(g_number) # 结果随机 可能小于等于2000000
2、递归同步锁
在同步锁的基础上可以做到连续重复使用多次acquire()后再重复使用多次release()的操作,但是一定要注意加锁次数和解锁次数必须一致,否则也将引发死锁现象。
递归锁RLock:它内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。
3、条件锁
条件锁是在递归锁的基础上增加了能够暂停线程运行的功能。并且我们可以使用wait()与notify()来控制线程执行的个数。
注意:条件锁可以自由设定一次放行几个线程。
import threadingcurrentRunThreadNumber = 0
maxSubThreadNumber = 10def task():global currentRunThreadNumberthread_name = threading.currentThread().namewith condLock:print("线程开始启动,并马上进入等待状态 : %s" % thread_name)condLock.wait() # 暂停线程运行、等待唤醒print("线程唤醒了,开始运行后面的代码 : %s" % thread_name)currentRunThreadNumber += 1if __name__ == "__main__":condLock = threading.Condition()for i in range(maxSubThreadNumber):subThreadIns = threading.Thread(target=task)subThreadIns.start()while currentRunThreadNumber < maxSubThreadNumber:notifyNumber = int(input("请输入要唤醒几个线程:"))with condLock:condLock.notify(notifyNumber) # 放行print("main thread run end")
4、事件锁
事件锁是基于条件锁来做的,它与条件锁的区别在于一次只能放行全部,不能放行任意个数量的子线程继续运行。
我们可以将事件锁看为红绿灯,当红灯时所有子线程都暂停运行,并进入“等待”状态,当绿灯时所有子线程都恢复“运行”。
import threadingmaxSubThreadNumber = 3def task():thread_name = threading.currentThread().nameprint("线程开始启动,并马上进入等待状态 : %s" % thread_name)eventLock.wait() # 暂停运行,等待绿灯print("第一次绿灯打开,线程往下走:%s" % thread_name)eventLock.wait() # 暂停运行,等待绿灯print("第二次绿灯打开,线程往下走:%s" % thread_name)if __name__ == "__main__":eventLock = threading.Event()for i in range(maxSubThreadNumber):subThreadIns = threading.Thread(target=task)subThreadIns.start()eventLock.set() # 设置为绿灯eventLock.clear() # 设置为红灯eventLock.set()
5、信号量锁
Semaphore()
信号量锁也是根据条件锁来做的,它与条件锁和事件锁的区别如下:
- 条件锁:一次可以放行任意个处于“等待”状态的线程
- 事件锁:一次可以放行全部的处于“等待”状态的线程
- 信号量锁:通过规定,成批的放行特定(指定)个处于“上锁”状态的线程
import threading
import timemaxSubThreadNumber = 6def task():thread_name = threading.currentThread().namewith semaLock:print("线程获得锁,开始运行: %s" % thread_name)time.sleep(3)if __name__ == "__main__":semaLock = threading.Semaphore(2)for i in range(maxSubThreadNumber):subThreadIns = threading.Thread(target=task)subThreadIns.start()
相关文章:

Python零基础从小白打怪升级中~~~~~~~多线程
线程安全和锁 一、全局解释器锁 首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。 GIL全称global interpreter lock,全局解释器锁。 每个线程在执行的时候都需要先获取GIL,保证同一时刻只…...
【机器学习300问】67、均方误差与交叉熵误差,两种损失函数的区别?
一、均方误差(Mean Squared Error, MSE) 假设你是一个教练,在指导学生射箭。每次射箭后,你可以测量子弹的落点距离靶心的差距(误差)。MSE就像是计算所以射击误差的平方后的平均值。它强调了每一次偏离靶心的…...

SAP打印输出设置
SAP打印输入有很多方式,适合不同的应用场景。 一.打印输出总体概览图 二.前台打印 这个是比较常见的,前端打印的出现减轻了管理员的工作量,用户可以选择自己电脑上的打印机输出,不需要所有打印机都在SAP平台中进行配置࿰…...

qt对json文件下,qdatetime时间的正确读写方式
qt 对json文件下qdatetime时间的正确读写方式 被搞了很长时间,最后发现是需要控制格式。 正确方式 // read QByteArray localBytes mapJson["playTime"].toString().toLocal8Bit(); char* char_time localBytes.data(); std::string str_time char_…...

【系统分析师】计算机网络
文章目录 1、TCP/IP协议族1.1 DHCP协议1.2 DNS协议1.3网络故障诊断 2、网路规划与设计2.1逻辑网络设计2.2物理网络设计2.3 分层设计 3、网络接入3.1 接入方式3.2 IPv6地址 4、综合布线技术5、物联网5.1物联网概念与分层5.2 物联网关键技术 6、云计算7、网络存储技术(…...

DDoS攻击愈演愈烈,谈如何做好DDoS防御
DDoS攻击是目前最常见的网络攻击方式之一,各种规模的企业包括组织机构都在受其影响。对于未受保护的企业来讲,每次DDoS攻击的平均成本为20万美元。可见,我们显然需要开展更多的DDoS防御工作。除考虑如何规避已发生的攻击外,更重要…...

48.基于SpringBoot + Vue实现的前后端分离-雪具销售系统(项目 + 论文PPT)
项目介绍 本站是一个B/S模式系统,采用SpringBoot Vue框架,MYSQL数据库设计开发,充分保证系统的稳定性。系统具有界面清晰、操作简单,功能齐全的特点,使得基于SpringBoot Vue技术的雪具销售系统设计与实现管理工作系统…...

P8715 [蓝桥杯 2020 省 AB2] 子串分值 (双边检测)
# [蓝桥杯 2020 省 AB2] 子串分值 ## 题目描述 对于一个字符串 $S$, 我们定义 $S$ 的分值 $f(S)$ 为 $S$ 中恰好出现一次的字符个数。例如 $f\left({ }^{\prime \prime} \mathrm{aba}{ }^{\prime \prime}\right)1$,$f\left({ }^{\prime \prime} \mathrm{abc}{ }^{…...

(十四)C++自制植物大战僵尸游戏windows平台视频播放实现
植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/8UFMs VLC库 在Cocos2d-x游戏开发框架中,没有实现windows平台视频播放的功能,需要自定义实现。在本项目中使用vlc库实现windows平台的视频播放功能。 vlc官网:网址 下载完成后&#x…...

深入理解GCC/G++在CentOS上的应用
文章目录 深入理解GCC/G在CentOS上的应用编译C和C源文件C语言编译C语言编译 编译过程的详解预处理编译汇编链接 链接动态库和静态库静态库和动态库安装静态库 结论 深入理解GCC/G在CentOS上的应用 在前文的基础上,我们已经了解了CentOS的基本特性和如何在其上安装及…...

C语言【复合类型(自定义类型)】
一、结构体 结构体(struct)可以理解为用户自定义的特殊的复合的“数据类型”; 1. 结构体变量的定义和初始化 定义结构体变量的方式: 先声明结构体类型再定义变量名 在声明类型的同时定义变量 // 结构体类型的定义 struct stu {char name[50];int age;…...

【python】初识爬虫
Python爬虫介绍 目录 一、概述二、Python爬虫的基本构成爬虫引擎解析器数据存储三、Python爬虫的主要技术请求与响应URL管理页面解析数据存储四、Python爬虫的应用场景数据采集搜索引擎竞品分析价格监控五、Python爬虫的开发流程六、常用的库...

提高小红书底层逻辑认知+短视频思路打爆笔记+纯带货笔记起号(8节课)
课程内容: 小红书如何0押金开通店铺 .mp4 店铺基础搭建.mp4 小红营的流量从哪里来? 如何用养号发现优质的对标笔记?养号的真正意义是什么?.mp4 什么叫垂类标签? 账号如何打上正确的标签 .mp4 前期笔记是否挂车?不同类型的笔记审核标准是什么?.mp4 如何用…...

力扣:219. 存在重复元素 II
力扣:219. 存在重复元素 II 给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 i 和 j ,满足 nums[i] nums[j] 且 abs(i - j) < k 。如果存在,返回 true ;否则,返回 false 。 …...

程序安装 - 笔记
1 程序IOServer,依赖自己开发libs7.so.如何安装程序 要安装依赖于自己开发的 libs7.so 的程序 IOServer,你需要执行以下步骤: 编译 IOServer 程序:确保 IOServer 程序的源代码处于可用状态,并且已经编译成可执行文件。这可能需要执行诸如 make 或者其他编译命令,具体取决…...

文心一言 vs. GPT-4: 全面比较
1. 训练数据和预训练 文心一言 训练数据:文心一言是由中国研究人员开发的中文语言模型。它主要在大量古典中文文学作品上进行训练,包括诗歌、散文和历史文本。这些文学作品涵盖了丰富的中文语言和文化,使得文心一言在传统文化方面具有独特优…...

图书管理系统概述
自友图书馆管理系统解决方案适用于中小学、大中专院校以及企事业单位中小型图书馆的自动化管理需求,其功能覆盖了图书馆自动化集成管理业务流程所包括的所有环节。《图书馆管理系统》首先应该按照我国图书馆行业通用CNMARC格式及《中图法第四版》行业标准开发而成,支…...

中国老铁路增开对国际旅客列车开行
4月13日,中老铁路国际旅客列车开行一周年之际,中老两国铁路部门在中国西双版纳至老挝琅勃拉邦两大著名旅游城市间增开1对国际旅客列车,旅客乘火车可实现两地间当日往返。标题:古道新程——中国老铁路增开国际旅客列车 在这个日新月…...

搭建个人智能家居 4 -WS2812B-RGB灯
搭建个人智能家居 4 - WS2812B-RGB灯 前言说明ESPHomeHomeAssistant 前言 上一篇文章我们已经完成了第一个外设的添加(一个LED灯),今天接着来“壮大”这个系统,添加第二个外设“RGB灯”。 环境搭建可以回顾前面的文章。前文回顾&…...

C++类与对象(中)②
目录 1.赋值运算符重载 1.1运算符重载 1.2赋值运算符重载 1.2.1赋值运算符重载格式 1.2.2赋值运算符只能重载成成员函数不能重载成全局函数 1.2.3同拷贝函数一样,如果类是形如日期类这样变量全是内置类型的,赋值运算符就必须自己实现,…...

Qt——xml文件生成DBus接口
1. 如何根据xml文件生成Dbus接口 要使用 XML 文件生成 D-Bus 接口,你可以按照以下步骤操作: 步骤 1: 准备 XML 文件 确保你的 XML 文件遵循 D-Bus 的接口描述规范。这通常包括定义接口、方法、信号和属性。一个基本的例子如下: <!DOCTYPE…...

初识SpringMVC(SpringMVC学习笔记一)
1 、还是熟悉的配方,先创建一个父Maven项目(忘记怎么创建项目了就去前面翻笔记),导入通用的配置依赖 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instan…...

FreeRTOS任务管理
1. 任务状态理论讲解 定时器职中断周期此处的1000Hz表示的是没次间隔1毫秒就记一次数(在FreeConfig.h)文件中进行配置 #define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) 判断是否需要任务切换在FreeRTOS里面每次间隔1毫秒切换一次(程序…...

Qwen1.5大语言模型微调实践
在人工智能领域,大语言模型(Large Language Model,LLM)的兴起和广泛应用,为自然语言处理(NLP)带来了前所未有的变革。Qwen1.5大语言模型作为其中的佼佼者,不仅拥有强大的语言生成和理…...

购物车实现
目录 1.购物车常见的实现方式 2.购物车数据结构介绍 3.实例分析 1.controller层 2.service层 1.购物车常见的实现方式 方式一:存储到数据库 性能存在瓶颈方式二:前端本地存储 localstorage在浏览器中存储 key/value 对,没有过期时间。s…...

HTML5漫画风格个人介绍源码
源码介绍 HTML5漫画风格个人介绍源码,源码由HTMLCSSJS组成,记事本打开源码文件可以进行内容文字之类的修改,双击html文件可以本地运行效果,也可以上传到服务器里面,重定向这个界面 效果截图 源码下载 HTML5漫画风格…...

工业数学模型——高炉煤气发生量预测(三)
1、工业场景 冶金过程中生产的各种煤气,例如高炉煤气、焦炉煤气、转炉煤气等。作为重要的副产品和二次能源,保证它们的梯级利用和减少放散是煤气能源平衡调控的一项紧迫任务,准确的预测煤气的发生量是实现煤气系统在线最优调控的前提。 2、…...

pnpm - Failed to resolve loader: cache-loader. You may need to install it.
起因 工作原因需要研究 vue-grid-layout 的源码,于是下载到本地。因为我习惯使用 pnpm,所以直接用 pnpm i 安装依赖,npm run serve 启动失败。折腾了一番没成功。 看到源码里有 yarn.lock,于是重新用 yarn install 安装依赖&…...

CSS transition和animation的用法和区别
Transition和Animation在CSS中都是用于实现元素状态变化的效果,但它们在用法和特性上存在明显的区别。 Transition transition是过度属性,主要强调的是元素状态的过渡效果。 它通常用于在元素的状态发生变化时,平滑地过渡到一个新的状态。…...

书籍推荐(附上每本书的看点)
1、《FPGA深度解析》,这本书的FIFO部分我觉得讲得很好; 2、《verilog数字系统设计教程》,夏宇闻老师的蓝皮书,这本书里包含很多考试知识点; 3、《SOC设计方法和实现》郭炜老师写的,我觉得他的低功耗设计讲得很好; 《高级FPGA设计结…...