当前位置: 首页 > news >正文

Python并发编程 05 锁、同步条件、信号量、线程队列、生产者消费者模型

文章目录

  • 一、基础概念
  • 二、同步锁
  • 三、线程死锁和递归锁
  • 四、同步条件(event)
  • 五、信号量
  • 六、线程队列(queue)
    • 1、常用方法
    • 2、queue模块的三种模式
      • (1)FIFO队列
      • (2)LIFO队列
      • (3)按优先级
  • 七、生产者消费者模型

一、基础概念

①并发: 系统在一段时间内,处理多个任务的能力
②并行: 系统在同一时刻,处理多个任务的能力
③同步: 当进程执行到一个IO(等待外部数据)的时候,等待,不继续向下运行。
④异步: 当进程执行到一个IO(等待外部数据)的时候,不等待,而是先执行其他代码,一直到数据接收成功,再回来处理。
⑤GIL: 全局解释锁。同一时刻,在同一进程下,如果有多个线程,但CPU只能执行其中一个。
⑥任务分两种: IO密集型和计算密集型。
  对于IO密集型的任务,python的多线程可以加快速度,可以采用多进程+协程处理。
  对于计算密集型的任务,python的多线程反而因为切换的开销,增加处理时间。

二、同步锁

同步锁也叫互斥锁
(1)未加同步锁情形

import threading
import timedef sub():global numtemp = numtime.sleep(0.001)num = temp -1num = 100l = []for i in range(100):t = threading.Thread(target=sub)t.start()l.append(t)for t in l:t.join()print(num)  # 98 输出结果不确定

上述代码中,各个线程在时间片轮转时,自己可能还没执行完,造成num值还未改变,就切换到其他线程了。所以num的值输出不固定(取决于cpu的执行速度)
(2)加同步锁情形

import threading
import timedef sub():global numlock.acquire() # 申请同步锁temp = numtime.sleep(0.001)num = temp - 1lock.release() # 释放同步锁num = 100l = []
lock = threading.Lock() # 创建同步锁对象for i in range(100):t = threading.Thread(target=sub)t.start()l.append(t)for t in l:t.join()print(num)  # 0

在执行 lock.acquire() 后,线程不允许被切换,在执行 lock.release() 后,才允许切换线程。

三、线程死锁和递归锁

在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。这两个线程在无外力作用下将一直等待下去。例:

import threading
import timeclass MyThread(threading.Thread):def actionA(self):A.acquire()print(self.name,"actionA gotA",time.strftime("%X"))time.sleep(1)B.acquire()print(self.name, "actionA gotB", time.strftime("%X"))time.sleep(1)B.release()A.release()def actionB(self):B.acquire()print(self.name, "actionB gotB", time.strftime("%X"))time.sleep(1)A.acquire()print(self.name, "actionB gotA", time.strftime("%X"))time.sleep(1)A.release()B.release()def run(self):self.actionA()self.actionB()if __name__ == '__main__':A = threading.Lock()B = threading.Lock()L = []for i in range(3):t = MyThread()t.start()L.append(t)for i in L:i.join()print("ending...")

第一个线程在执行完actionA方法的B.release()时,第二个线程开始执行actionA方法的A.acquire()。然后两个线程并发运行,直到第一个线程执行actionB方法的A.acquire(),第二个线程执行actionA方法的B.acquire,形成死锁。
解决方法——递归锁
递归锁可以看成有个计数器默认count=0,执行acquire时,count+1;执行release时,count-1。只有当count=0时,才允许新的线程执行acquire后续语句。

import threading
import timeclass MyThread(threading.Thread):def actionA(self):r_lock.acquire()    # count=1print(self.name,"actionA gotA",time.strftime("%X"))time.sleep(1)r_lock.acquire()    # count=2print(self.name, "actionA gotB", time.strftime("%X"))time.sleep(1)r_lock.release()    # count=1r_lock.release()    # count=0def actionB(self):r_lock.acquire()print(self.name, "actionB gotB", time.strftime("%X"))time.sleep(1)r_lock.acquire()print(self.name, "actionB gotA", time.strftime("%X"))time.sleep(1)r_lock.release()r_lock.release()def run(self):self.actionA()self.actionB()if __name__ == '__main__':r_lock = threading.RLock()L = []for i in range(3):t = MyThread()t.start()L.append(t)for i in L:i.join()print("ending...")

四、同步条件(event)

event是一个简单的同步对象。
event可以使两个线程同步。
event = threading.Event() 创建同步条件对象
执行event.wait()的线程将等待flag被设定,而阻塞。event.set() 设定flag。当flag被设定的时候,执行event.wait()的线程将不被阻塞,相当于pass。当执行event.clear(),flag将被清除,event.wait()将继续阻塞。多个线程可以等候同一个event对象。

import threading,time
class Boss(threading.Thread):def run(self):print("BOSS:今晚大家都要加班到22:00。")print("first:",event.isSet())# Falseevent.set()time.sleep(3)print("BOSS:<22:00>可以下班了。")print("second:",event.isSet())  # Falseevent.set()class Worker(threading.Thread):def run(self):event.wait()#    一旦flag被设定,event等同于passprint("Worker:哎……命苦啊!")time.sleep(1)event.clear()event.wait()print("Worker:OhYeah!")if __name__=="__main__":event=threading.Event()threads=[]for i in range(5):threads.append(Worker())threads.append(Boss())for t in threads:t.start()for t in threads:t.join()print("ending.....")

五、信号量

Semaphore(count)设定一个计数器count,然后每执行acquire()时,count-1,执行release()时,count+1。当count=0时,再执行acquire()将阻塞线程。相当于同步锁的一个扩展,同步锁的count最大等于1.。

import threading,timeclass myThread(threading.Thread):def run(self):if semaphore.acquire(): # 允许5个线程同时进print(self.name)time.sleep(2)semaphore.release() # 5个线程同时释放锁if __name__=="__main__":semaphore=threading.Semaphore(5)    # 相当于申请了5把锁thrs=[]for i in range(100):thrs.append(myThread())for t in thrs:t.start()

六、线程队列(queue)

同一个进程下的多个线程,共享该queue数据

1、常用方法

put(item): 在队列队尾插入一个item。其参数block默认为True,如果队列已满,将阻塞线程。设置block=False,队列已满,将不会阻塞线程,而是引发Full异常。
get(): 将一个值从队列中取出,其参数block默认为True,如果队列已空,将阻塞线程。设置block=False,队列已空,将不会阻塞线程,而是引发Empty异常。
qsize(): 返回队列大小(实际存储数据的个数)
empty(): 如果队列为空,返回True,反之False
full(): 如果队列已满,返回True,反之False
full: 与maxsize大小对应
get_nowait(): 相当于get(block=False)
**put_nowait(item):**相当于put(item,block=False)
task_done() 和 join(): 每当向队列中put()一个item时,未完成任务的计数unfinished_tasks就会加1。调用一次task_done(),unfinished_tasks会减1:
  当unfinished_tasks=0时,join()相当于pass。
  当unfinished_tasks>0时,join()会阻塞线程。
  当调用task_done()次数多于put()时,会引发异常ValueError: task_done() called too many times

2、queue模块的三种模式

(1)FIFO队列

先进先出

import queueq = queue.Queue(3) # FIFO模式
# Queue(maxsize)的参数maxsize为队列最大长度,缺省,则队列长度无限长。q.put(12)
q.put("hello")
q.put({"age":18})while True:data = q.get()print(data)print("---------")'''
12
---------
hello
---------
{'age': 18}
---------
'''

(2)LIFO队列

后进先出,类似于栈

import queueq = queue.LifoQueue() # LIFO模式q.put(12)
q.put("hello")
q.put({"age":18})while True:data = q.get()print(data)print("---------")'''
{'age': 18}
---------
hello
---------
12
---------
'''

(3)按优先级

设置优先级数字越小,越先出来

import queueq = queue.PriorityQueue() # 按优先级模式q.put([3,12])
q.put([2,"hello"])
q.put([4, {"age": 18}])while True:data = q.get()print(data)print("---------")'''
[2, 'hello']
---------
[3, 12]
---------
[4, {'age': 18}]
---------
'''

七、生产者消费者模型

生产数据的线程就是生产者,消费数据的线程就是消费者。为了解决生产者生产速度和消费者消费数据速度不均衡的问题,需要引入一个阻塞队列作为缓冲区。

import threading, queue
import timedef consumer(q):while True:item = q.get()print(f"Consume {item}\n",end="")time.sleep(1)q.task_done()def producer(q):for i in range(10):q.put(i)print("in production...\n", end="")q.join()print("finish!\n",end="")q = queue.Queue()t1 = threading.Thread(target=consumer, args=(q,),daemon=True)
t2 = threading.Thread(target=consumer, args=(q,),daemon=True)t1.start()
t2.start()producer(q)
print("end")

相关文章:

Python并发编程 05 锁、同步条件、信号量、线程队列、生产者消费者模型

文章目录 一、基础概念二、同步锁三、线程死锁和递归锁四、同步条件&#xff08;event&#xff09;五、信号量六、线程队列&#xff08;queue&#xff09;1、常用方法2、queue模块的三种模式&#xff08;1&#xff09;FIFO队列&#xff08;2&#xff09;LIFO队列&#xff08;3&…...

UIKit之UIButton

功能需求&#xff1a; 点击按钮切换按钮的文字和背景图片&#xff0c;同时点击上下左右可以移动图片位置&#xff0c;点击加或减可以放大或缩小图片。 分析&#xff1a; 实现一个UIView的子类即可&#xff0c;该子类包含多个按钮。 实现步骤&#xff1a; 使用OC语言&#xf…...

阿里云VOD视频点播流程(2)

二、视频点播 1、入门代码 基于OSS原生SDK上传 &#xff0c;参考文档&#xff1a;https://help.aliyun.com/zh/vod/user-guide/upload-media-files-by-using-oss-sdks?spma2c4g.11186623.0.0.1f02273fj4lxNJ 视频点播面向开发者提供了丰富的上传方式&#xff0c;其中上传SDK&…...

在Ubuntu上搭建幻兽帕鲁服务器

简介 幻兽帕鲁是一款多人在线角色扮演游戏&#xff0c;玩家可以捕捉和训练各种各样的幻兽&#xff0c;并与其他玩家进行战斗和交易。如果您想拥有自己的幻兽帕鲁服务器&#xff0c;可以按照以下步骤在 Ubuntu 上进行搭建。 准备工作 在开始之前&#xff0c;您需要准备以下几…...

Java中常用类String的不可变性详解

目录 一、String类的概述 二、String不可变性的原理 三、String不可变性的优点 四、String不可变性的缺点及解决方案 五、总结 一、String类的概述 在Java中&#xff0c;String类是一个代表字符串的类。它是Java核心API的一部分&#xff0c;用于处理文本数据。String对象…...

uniapp 自定义App UrlSchemes

需求&#xff1a;外部浏览器H5页面&#xff0c;跳转到uniapp开发的原生app内部。 1、uniapp内部的配置&#xff1a; &#xff08;1&#xff09;打开manifest->App常用其他设置&#xff0c;如下&#xff0c;按照提示输入您要设置的urlSchemes&#xff1a; &#xff08;2&am…...

MSP430环境搭建

1.下载ccs编译器 注意&#xff1a;安装路径和工作路径不能出现中文&#xff01; 没有说明的步骤就点next即可&#xff01; 1.1下载适合自己电脑的压缩包。 下载好压缩包后解压&#xff0c;点击有图标进行安装。 1.2创建一个文件夹用于安装编译器位置 选择安装地址&#xff0…...

【Qt C++实现蓝牙互联】

在 Qt C++ 中实现蓝牙互联可以通过 Qt 的蓝牙模块来实现。下面是一个简单的示例,演示如何在 Qt C++ 中使用蓝牙模块进行蓝牙互联,实现搜索设备、连接设备等功能。 // main.cpp #include <QCoreApplication> #include <QBluetoothDeviceDiscoveryAgent> #include…...

AI绘画已如此厉害,为何我们仍需学习绘画?

在这个AI技术日新月异的时代&#xff0c;AI绘画能力的大幅提升已经不是什么新鲜事。它们以惊人的速度和惊人的精细度完成作品&#xff0c;让不少人感叹&#xff1a;“这是不是意味着&#xff0c;未来绘画将完全由AI接管&#xff0c;人类的创作将变得无足轻重&#xff1f;”在这…...

Android 实现背景图片不被拉伸的效果 9-patch图片 .9图

今天碰到个需求&#xff0c;要求不同手机分辨率背景照片不能被拉伸&#xff0c;除了调用系统方法计算当前屏幕大小这个方法外还有一个就是9-patch图片&#xff0c;可以实现除了icon剩下的部位被缩放。 方法&#xff1a;资源文件右击找到9-patch&#xff0c;转为XXX.9.png照片 …...

Java EE/Jakarta EE范畴一览

Java EE&#xff08;Java Platform, Enterprise Edition&#xff09;现在已经改名为Jakarta EE&#xff0c;是一套用于开发企业级应用的标准Java平台。它扩展了Java SE&#xff08;Standard Edition&#xff09;&#xff0c;添加了支持大规模、多层次、可靠、安全、可伸缩和可管…...

洛谷 P3391:文艺平衡树 ← Splay树模板题

【题目来源】https://www.luogu.com.cn/problem/P3391【题目描述】 您需要写一种数据结构&#xff08;可参考题目标题&#xff09;&#xff0c;来维护一个有序数列。 其中需要提供以下操作&#xff1a;翻转一个区间&#xff0c;例如原有序序列是 5 4 3 2 1&#xff0c;翻转区间…...

【高校科研前沿】北师大陈晋教授团队在遥感顶刊发表最新成果:ClearSCD模型:在高空间分辨率遥感影像中综合利用语义和变化关系进行语义变化检测

01文章简介 论文名称&#xff1a;The ClearSCD model: Comprehensively leveraging semantics and change relationships for semantic change detection in high spatial resolution remote sensing imagery&#xff08;ClearSCD模型&#xff1a;在高空间分辨率遥感影像中综合…...

关于YOLO8学习(五)安卓部署ncnn模型--视频检测

教学视频地址 B站 前文 关于YOLO8学习(一)环境搭建,官方检测模型部署到手机 关于YOLO8学习(二)数据集收集,处理 关于YOLO8学习(三)训练自定义的数据集 关于YOLO8学习(四)模型转换为ncnn 简介 本文将会讲解: (1)使用前文生成的ncnn模型,部署到安卓端,并且实…...

从哪些方面可以看出光伏的未来发展好?

光伏发电是一种基于半导体材料的光伏效应将太阳光转化为直流电能的发电技术。近年来&#xff0c;随着全球对可再生能源和环境保护的关注度不断提升&#xff0c;光伏发电行业发展迅速&#xff0c;成为未来能源领域的重要发展方向。 首先&#xff0c;从能源角度来看&#xff0c;光…...

VBA_MF系列技术资料1-605

MF系列VBA技术资料1-605 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-0…...

算法题① —— 数组专栏

1. 滑动窗口 1.1 长度最小的子数组 力扣&#xff1a;https://leetcode.cn/problems/minimum-size-subarray-sum/description/ int minSubArrayLen(int s, vector<int>& nums) {int result INT32_MAX; int sum 0; // 子序列的数值之和int subLength 0; // 子序列…...

算法学习笔记(差分约束系统)

前置&#xff1a;spfa 从例题入手&#xff1a; 【模板】差分约束系统 | StarryCoding 题目描述 给定 n n n未知量和一个大小为 m m m的不等式&#xff08;或等式&#xff09;组&#xff0c;请你判断这个不等式&#xff08;或等式&#xff09;组是否有解。 1 1 1 i i i j …...

HCIP的学习(14)

过滤策略—filter-policy ​ 思科中&#xff1a;分发列表 ​ 过滤策略是只能够针对于路由信息进行筛选&#xff08;过滤&#xff09;的工具&#xff0c;而无法针对于LSA进行过滤。 在R4的出方向上配置过滤策略&#xff0c;使得R1不能学习到23.0.0.0/24路由信息1、抓取流量 […...

行业新应用:电机驱动将成为机器人的动力核心

电机已经遍布当今社会人们生活的方方面面&#xff0c;不仅应用范围越来越广&#xff0c;更新换代的速度也日益加快。按照工作电源分类&#xff0c;可以将它划分为直流电机和交流电机两大类型。直流电机中&#xff0c;按照线圈类型分类&#xff0c;又可以分为有铁芯的电机、空心…...

避开这些坑!在Quartus中设计硬布线CPU时,我的控制器和PC模块是如何调试的

硬布线CPU调试实战&#xff1a;从BEQ失效到波形分析的深度排错指南 当你在Quartus中完成单周期CPU的数据通路搭建&#xff0c;满心欢喜点击仿真按钮时&#xff0c;最令人崩溃的莫过于看到BEQ指令毫无反应、存储器读写数据错乱、或者PC计数器像脱缰野马般失去控制。这些看似简单…...

终极解决方案:VisualCppRedist AIO一站式修复Windows运行库问题

终极解决方案&#xff1a;VisualCppRedist AIO一站式修复Windows运行库问题 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否经常在Windows系统上遇到"…...

终极指南:使用LSLib专业工具进行《神界原罪》和《博德之门3》MOD开发

终极指南&#xff1a;使用LSLib专业工具进行《神界原罪》和《博德之门3》MOD开发 【免费下载链接】lslib Tools for manipulating Divinity Original Sin and Baldurs Gate 3 files 项目地址: https://gitcode.com/gh_mirrors/ls/lslib LSLib是一款功能强大的开源游戏MO…...

抖音无水印下载神器:3分钟搞定批量下载,小白也能轻松上手

抖音无水印下载神器&#xff1a;3分钟搞定批量下载&#xff0c;小白也能轻松上手 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser …...

ClawGuard Web:构建AI技能安全扫描平台,从代码安全到信任生态

1. 项目概述&#xff1a;ClawGuard Web 安全技能注册平台如果你在 OpenClaw 生态里开发或使用技能&#xff0c;那你肯定遇到过这个头疼的问题&#xff1a;从 ClawHub 或者 GitHub 上找到一个看起来不错的技能&#xff0c;但心里总犯嘀咕——这代码里会不会藏着恶意后门&#xf…...

OBS多平台直播终极指南:obs-multi-rtmp插件让你一键同步推流到各大平台

OBS多平台直播终极指南&#xff1a;obs-multi-rtmp插件让你一键同步推流到各大平台 【免费下载链接】obs-multi-rtmp OBS複数サイト同時配信プラグイン 项目地址: https://gitcode.com/gh_mirrors/ob/obs-multi-rtmp 还在为多平台直播的繁琐配置而烦恼吗&#xff1f;obs…...

用Python+OpenCV给《梦幻西游》写个自动挖图脚本(附完整代码与避坑指南)

用PythonOpenCV实现《梦幻西游》自动挖宝图的全流程实战 最近在技术社区看到不少关于游戏自动化的讨论&#xff0c;尤其是像《梦幻西游》这类经典MMORPG&#xff0c;很多开发者尝试用计算机视觉技术实现自动化操作。作为一个长期关注OpenCV应用的开发者&#xff0c;我花了三周…...

Notflix高级技巧:5种高效搜索和流媒体传输方法

Notflix高级技巧&#xff1a;5种高效搜索和流媒体传输方法 【免费下载链接】notflix Notflix is a shell script to search and stream torrent. 项目地址: https://gitcode.com/gh_mirrors/no/notflix Notflix是一款强大的shell脚本工具&#xff0c;能够帮助用户快速搜…...

别再只盯着信号强度了!深入浅出解读LoRa天线S11、驻波比与回波损耗

别再只盯着信号强度了&#xff01;深入浅出解读LoRa天线S11、驻波比与回波损耗 当你的LoRa设备通信距离突然缩水&#xff0c;或是信号时断时续&#xff0c;大多数工程师的第一反应往往是检查发射功率和环境干扰。但真正的高手会拿起矢量网络分析仪&#xff0c;直击问题核心——…...

【2026年携程暑期实习- 5月10日-第四题-单数组交换】(题目+思路+JavaC++Python解析+在线测试)

题目内容 游游有两个长度同为 nnn 的整数数组 aaa 和 bbb。她会对数组...