Python 多线程、多进程和协程
一、多线程
threading 模块
threading 模块对象
| 对象 | 描述 |
|---|---|
| Thread | 表示一个执行线程的对象 |
| Lock | 锁原语对象(与 thread 模块中的锁一样) |
| RLock | 可重入锁对象,使单一线程可以(再次)获得已持有的锁(递归锁) |
| Condition | 条件变量对象,使得一个线程等待另一个线程苗族特定的条件,比如改变状态或某个数据值 |
| Event | 添加变量的通用版本,任意数量的线程等待某个时间的发生,在该事件发生后所有线程将被激活 |
| Semaphore | 为线程键共享的有限资源提供一个 计算器(信号量),如果没有可用资源时会被阻塞 |
| BoundedSemaphore | 与 Semaphore 相似,不过不允许超过初始值 |
| Timer | 与 Thread 相似,在运行前要等待一段时间 |
| Barrier | 创建一个障碍,必须达到指定数量的线程后才可以继续 |
守护线程
1、thread 模块不支持守护线程,当主线程退出后,所有子线程也会退出,不管其是否在工作
2、threading 模块支持守护线程:等待一个客户端请求服务的服务器,如果客户端没有请求,守护线程是空闲的,如果把一个线程设置为守护线程,就表示这个线程不重要的。进程退出时不需要等待这个线程执行完成。
如果主线程退出时,不需要等待某些子线程完成,可以将子线程设置为 守护线程,标记为真时,表示该线程不重要。在启动线程前执行 thread.daemon=True 可以设置守护线程,检查线程的守护状态也可以判断它。
Thread 类
threading 模块的 Thread 类是主要的执行对象,下面是 Thread 对象的属性和方法列表:
| 属性 | 描述 | 方法 | 描述 |
|---|---|---|---|
| name | 线程名 | start() | 开始执行该线程 |
| ident | 线程的标识符 | run() | 定义线程功能的方法,通常在子类中被应用开发者重写 |
| daemon | 布尔值,表示这个线程是否是守护线程 | join(timeout=None) | 直至启动的线程终止之前一直挂起,除非给出 timeout,否则一直阻塞 |
Thread(group=None, target=None, name=None, agrs=(), kwargs={}, verbose=None, daemon=None)# 实例化一个线程对象,需要一个可调用的 target,一般是函数,及其参数 args(元组)或 kwargs
# 也可以传递 name 或 group 参数,daemon 将会设定 thread.daemon 属性/标志
创建线程的三种方法
创建 Thread 实例,传递给它一个函数
import threading
from time import sleep, ctimeloops = [4, 2]
def loop(nloop, nsec):print('loop 函数开始执行 %(nloop)s,时间: %(ctime)s' % {'nloop': nloop, 'ctime': ctime()})sleep(nsec)print('loop 函数结束执行 %(nloop)s,时间: %(ctime)s' % {'nloop': nloop, 'ctime': ctime()})def main():print('主函数开始执行:', ctime())threads = []nloops = range(len(loops))for i in nloops:t = threading.Thread(target=loop, args=(i, loops[i]))threads.append(t)for i in nloops:threads[i].start()for i in nloops:threads[i].join()print('程序结束:', ctime())if __name__ == '__main__':main()
上述程序将生成两个线程,将其添加到一个列表中,循环启动 start()。join() 方法将会程序挂起,会等待所有线程结束或超时。因此要比
等待释放的无限循环更加清晰。
运行结果如下:
主函数开始执行: Sat Sep 7 17:31:40 2019
loop 函数开始执行 0,时间: Sat Sep 7 17:31:40 2019
loop 函数开始执行 1,时间: Sat Sep 7 17:31:40 2019
loop 函数结束执行 1,时间: Sat Sep 7 17:31:42 2019
loop 函数结束执行 0,时间: Sat Sep 7 17:31:44 2019
程序结束: Sat Sep 7 17:31:44 2019
创建 Thread 的实例,传给它一个可调用的类实例
import threading
from time import sleep, ctimeloops = [4, 2]class ThreadFunc:def __init__(self, func, args, name=''):self.name = nameself.func = funcself.args = argsdef __call__(self, *args, **kwargs):"""当调用 ThreadFunc() 时会自动执行 fun()"""self.func(*self.args)def loop(nloop, nsec):print('loop 函数开始执行 %(nloop)s,时间: %(ctime)s' % {'nloop': nloop, 'ctime': ctime()})sleep(nsec)print('loop 函数结束执行 %(nloop)s,时间: %(ctime)s' % {'nloop': nloop, 'ctime': ctime()})def main():print('主函数开始执行:', ctime())threads = []nloops = range(len(loops))for i in nloops: # 创建 Thread 的实例,传给它一个可调用的类实例t = threading.Thread(target=ThreadFunc(loop, (i, loops[i]), loop.__name__))threads.append(t)for i in nloops:threads[i].start()for i in nloops:threads[i].join()print('程序结束:', ctime())if __name__ == '__main__':main()
派生 Thread 的子类,并创建子类的实例
自定义的类要继承 threading.Thread,构造函数必须先调用其基类的构造函数,__call__() 在子类中必须要写 run()
import threading
from time import sleep, ctimeloops = [4, 2]class MyThread(threading.Thread):def __init__(self, func, args, name=''):threading.Thread.__init__(self)self.name = nameself.func = funcself.args = argsdef run(self, *args, **kwargs):self.func(*self.args)def loop(nloop, nsec):print('loop 函数开始执行 %(nloop)s,时间: %(ctime)s' % {'nloop': nloop, 'ctime': ctime()})sleep(nsec)print('loop 函数结束执行 %(nloop)s,时间: %(ctime)s' % {'nloop': nloop, 'ctime': ctime()})def main():print('主函数开始执行:', ctime())threads = []nloops = range(len(loops))for i in nloops:t = MyThread(loop, (i, loops[i]), loop.__name__)threads.append(t)for i in nloops:threads[i].start()for i in nloops:threads[i].join()print('程序结束:', ctime())if __name__ == '__main__':main()
threading 模块的其他函数
- active_count():当前活动的 Thread 对象个数
- current_thread:返回当前的 Thread 对象
- enumerate():返回当前活动的 Thread 对象列表
- settrace(func):为所有线程设置一个 trace 函数
- setprofile(func):为所有线程设置一个 profile 函数
- stack_size(size=0):返回新建线程的栈大小,或为后续创建的线程设定栈的大小为 size
启动和停止线程
import time
import threadingdef countdown(n):while n > 0:print('T-minus:%s,time:%s' % (n, time.ctime()))n -= 1time.sleep(1)if t.is_alive():print('Still running...')else:print('Completed')print('主线程...')if __name__ == '__main__':t = threading.Thread(target=countdown, args=(5, ))t.start()t.join()
判断一个线程是否存活,可以调用 is_alive() 方法,解释器会在所有线程都结束后才执行剩余的代码,如果需要长时间运行的线程
或者一直运行的后台任务,可以使用后台线程:
threading.Thread(target=countdown, args=(5, ), daemon=True)
后台线程无法等待,这些线程会在主线程终止时自动销毁。但是你也不能对线程做额外的高级操作,如:发送信号,调整它的调度,终止线程等。
join() 方法
join() 方法将悬挂当前子线程,直至所有子线程结束。
import time, threadingdef loop():print('thread %s is running...' % threading.current_thread().name)n = 0while n < 5:n += 1print('thread %s >>> %s' % (threading.current_thread().name, n))time.sleep(1)print('thread %s ended.' % threading.current_thread().name)if __name__ == '__main__':print('thread %s is running>>>>' % threading.current_thread().name)t = threading.Thread(target=loop, name='LoopThread')t.start()t.join()print('thread %s ended>>>>' % threading.current_thread().name)
- name:指定子线程名字,不指定默认为 thread-1、thread-2
- MainThread 为主线程
- threading.current_thread().name:获取当前线程的实例(名字)
可以看到 join() 它将子线程添加到当前主线程中,并等待子线程终止,才允许它后面的代码:
thread MainThread is running>>>>
thread LoopThread is running...
thread LoopThread >>> 1
thread LoopThread >>> 2
thread LoopThread >>> 3
thread LoopThread >>> 4
thread LoopThread >>> 5
thread LoopThread ended.
thread MainThread ended>>>>
停止线程
如果线程执行一些如 I/O 这样的阻塞操作,通过轮询来终止线程将使得线程间的协调变得非常棘手。如,如果一个线程一直阻塞在一个 I/O
操作上,就永远无法返回检查自己是否已经被结束了。要正确处理这些问题,需要利用 超时循环 来小心操作线程:
class IOTask:def terminate(self):self._running = Falsedef run(self, sock):# sock is a socketsock.settimeout(5) # set timeout periodwhile self._running:try:data = sock.recv(8192)breakexcept socket.timeout:continue# continued processing...# Terminatedreturn
判断线程是否启动
启动了一个线程,但是你想知道它是否真的已经开始了,由于线程是独立运行的且状态不可预测的。如果程序中其他线程
要通过判断某个线程的状态来确定自己的下一步操作,这就会显得很棘手。
我们可以使用 Event 对象来解决这个问题,Event 对象包含一个可由线程设置信号的标志,允许线程等待某事的发生。
- 初始时,标志为假
- 标志未假时,这个线程会被一直阻塞直至标志为真
from threading import Thread, Event
import timedef countdown(n, started_evt):print('countdown 开始...')started_evt.set() # 设置标志为真while n > 0:print('T-minus', n)n -= 1time.sleep(1)started_evt = Event() # 创建 Event 对象print('启动 countdown 函数')
t = Thread(target=countdown, args=(5, started_evt))
t.start()# 等待线程开始
started_evt.wait()
print('countdown is running...')
可以看到 countdown is running... 一直在 countdown 开始... 输出之后才打印,这是因为 event 在协调线程。使得主线程要等
countdown() 函数输出启动信息后,才继续执行。
启动 countdown 函数
countdown 开始...
T-minus 5
countdown is running...
T-minus 4
T-minus 3
T-minus 2
T-minus 1
如果你将 started_evt.set() 注销掉,再运行程序,会发现程序一直被阻塞…,这是因为标志为假,线程被阻塞。
三、协程
Python 协程只能运行在时间循环中,但是一旦事件循环运行,又会阻塞当前任务。因此 动态添加任务/协程
需要再开一个线程,这个线程主要任务时运行事件循环,因为是无限循环,会阻塞当前线程:
import asyncio
from threading import Threadasync def production_task():i = 0while True:# 将 consumption 这个协程每秒注册一个运行到线程中的循环,thread_loop 每秒会获取一个一直打印 i 的无限循环任务# run_coroutine_threadsafe 这个方法只能用在运行在线程中的循环事件使用asyncio.run_coroutine_threadsafe(consumption(i), thread_loop)await asyncio.sleep(1) # 必须加 awaiti += 1async def consumption(i):while True:print('我是第{}任务'.format(i))await asyncio.sleep(1)def start_loop(loop):# 运行事件循环,loop 以参数的形式传递进来运行asyncio.set_event_loop(loop)loop.run_forever()thread_loop = asyncio.new_event_loop() # 获取一个事件循环
run_loop_thread = Thread(target=start_loop, args=(thread_loop,)) # 将每次事件循环运行在一个线程中,防止阻塞当前主线程
run_loop_thread.start() # 运行线程,同时协程事件循环也会运行advocate_loop = asyncio.get_event_loop() # 将生产任务的协程注册到这个循环中
advocate_loop.run_until_complete(production_task()) # 运行次循环
我是第0任务
我是第1任务
我是第0任务
我是第1任务
我是第0任务
我是第2任务
我是第3任务
我是第1任务
我是第0任务
四、五种 unix IO 模型
epoll 并不代表一定比 select 好(效率高)
- 在并发高的情况下,连接活跃度不是很高的情况下,epoll 比 select 好(比如:Web 连接,用户连接可能随时断开)
- 在并发性不高,同时连接活跃,select 比 epoll 好(比如:游戏连接,一般要保持持续连接)
select、poll、epoll 本质还是同步阻塞的,之所以能支持高并发,是因为一个进程能同时监听多个文件描述符
非阻塞 IO 不一定比阻塞好,因为它要一直循环检测服务器是否有数据返回,如果后续的程序不依赖前面的连接,其效率要高,要是依赖前面的程序,效率不一定要好。
相关文章:
Python 多线程、多进程和协程
一、多线程 threading 模块 threading 模块对象 对象描述Thread表示一个执行线程的对象Lock锁原语对象(与 thread 模块中的锁一样)RLock可重入锁对象,使单一线程可以(再次)获得已持有的锁(递归锁&#x…...
Xml 注解
文章目录XmlRootElement(name"MyRootElement")XmlAccessorType(XmlAccessType.FIELD)XmlElementXmlAttributeXmlValueXmlElementRefXmlRootElement(name“MyRootElement”) XmlRootElement(name"MyRootElement") public class AccessorType {public Strin…...
【CSS文字滚动】CSS实现文字横向循环无缝滚动,鼠标移入暂停移出继续(附实测源码)
CSS如何实现文字横向滚动滚动效果1、垃圾liMarquee(最好别用)2、css实现文字滚动,且鼠标移入移出暂停和继续HTML源码如下:CSS源码如下:JS源码如下:3、片尾彩蛋CSS实现文字横向循环无缝滚动,鼠标…...
不使用implements关键字实现实现类(类似于mapper)
首先,说明一下功能需求,平时定义一个接口,就要使用implements关键字来实现接口。那么,当不使用此关键字的时候,是否也能使相关接口也能够绑定实现类呢? 答案是肯定的。 此篇文章的主要功能有两个…...
antd4里table的滚动是如何实现的?
rc-table里Header、Footer、TableBody实现保持同频滚动的方法 场景:Header、Footer都有,Table设置了scrollX,才关注同频滚动 那么是如何实现的? 监听onScroll方法获取到滚动条向左的滚动的距离scrollLeft;同时给三个…...
抓取namenode 50070 jmx的指标信息
在生产实践过程中,需要把data退役之后需要停机下线,在下线之前需要确认机器是否已下线完成,要去namenode的50070界面上查看显然效率低,为了能够快速拿到节点信息,写了简单的脚本。jmx/50070还有很多信息可以获取&#…...
aspnetcore-browser-refresh.js和Visual Studio Browser Link
我在调试ASP.NET Core web应用时,发现请求的页面文档底部多了一部分文件,而在我的页面中却没有包含,故查询资料,在此记录: 图中,可以看到红框部分是多出来了2个脚本 1.aspnetcore-browser-refresh.js 这里…...
hadoop 集群常用命令(学习笔记) —— 筑梦之路
概念介绍 #HDFS 概述Hadoop Distributed File System,简称HDFS,是一个分布式文件系统。(1)NameNode(nn):存储文件的元数据,如文件名,文件目录结构,文件属性&…...
ARC142D Deterministic Placing
ARC142D Deterministic Placing 题目大意 有一棵nnn个顶点的树,每个点上最多放一张卡片,你可以做如下操作: 同时将所有的卡片移到它所在顶点的相邻的一个顶点上 一个操作我们说它是好的,当下列条件满足: 每条边最…...
阶段八:服务框架高级(第二章:分布式事务)
阶段八:服务框架高级(第二章:分布式事务)Day-分布式事务0.学习目标1.分布式事务问题1.1.本地事务1.2.分布式事务1.3.演示分布式事务问题2.理论基础2.1.CAP定理2.1.1.一致性2.1.2.可用性2.1.3.分区容错2.1.4.矛盾2.2.BASE理论2.3.解…...
RPC异步化原理
深入RPC,更好使用RPC,须从RPC框架整体性能考虑问题。得知道如何提升RPC框架的性能、稳定性、安全性、吞吐量及如何在分布式下快速定位问题。RPC框架如何压榨单机吞吐量? 1 前言 TPS一直上不去,压测时CPU压到40%~50%就…...
C# 多窗口切换的实现
1、目的在主窗口中根据不同的按钮选择不同的子窗口显示。2、实现(1)、创建Winform窗体程序,放入SplitContainer控件splitContainer1将窗体分成左右2部分;(2)、在左侧splitContainer1.panel1中放入3个Button…...
【深度学习】RNN
1. 什么是RNN 循环神经网络(Recurrent Neural Network, RNN)是一类以序列(sequence)数据为输入,在序列的演进方向进行递归(recursion)且所有节点(循环单元)按链式连接的递…...
招聘岗位,机会难得
岗位需求 费话不多说,直接上JD: 嵌入式开发工程师: 17:411.计算机、通信等相关专业。 2.熟悉网络基础知识,熟悉802.11a/b/g/n/ac协议,能通过抓包等分析手段排查定位各种wifi相关问题。 3.熟悉路由器主要功能及实现原…...
web打印的几种方法(2023)
在工作中出现web打印的情况是非常多的,其实这也是一个比较烦人的问题,这篇博客整理一下关于Web打印的一些方法或者方式。 1. window.print() 这个方法是用来打印网页的,页面上的其他的元素也会被打印处理,在打印的时候页眉页脚是…...
代码随想录算法训练营day44 | 动态规划之完全背包 518. 零钱兑换 II 377. 组合总和 Ⅳ
day44完全背包基础知识问题描述举个栗子518. 零钱兑换 II1.确定dp数组以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例推导dp数组377. 组合总和 Ⅳ1.确定dp数组以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例来推导dp数组完全背包基…...
IntelliJ IDEA 实用插件推荐(包含使用教程)
IntelliJ IDEA 实用插件推荐 背景:电脑重装了,重新下载了最新版的IntelliJ IDEA,感觉默认模式有点枯燥,于是决定从网上下载一些实用美观的插件优化自己以后吃饭的工具,现在推荐的都是目前还能用的(亲身实践…...
WideDeep模型
google提出的Wide&deep模型,将线性模型与DNN很好的结合起来,在提高模型泛化能力的同时,兼顾模型的记忆性。wide&deep这种将线性模型与DNN的并行连接模式,后来称为推荐领域的经典模式,奠定了后面深度学习模型的…...
nacos集群模式+keepalived搭建高可用服务
实际工作中如果nacos这样的核心服务停掉了或者整个服务器宕机了,那整个系统也就gg了,所以像这样的核心服务我们必须要搞个3个或者3个以上的nacos集群部署,实现高可用; 部署高可用版本之前,首先你要会部署单机版的naco…...
吉利「银河」负重突围
吉利控股集团最新公布的数据显示,2022年,吉利控股集团汽车总销量超230万辆,同比增长4.3%。其中,新能源汽车销量超64万辆,同比增长100.3%。 在中国本土市场,2022年吉利集团旗下品牌乘用车总交付量为135.84万…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
如何配置一个sql server使得其它用户可以通过excel odbc获取数据
要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据,你需要完成以下配置步骤: ✅ 一、在 SQL Server 端配置(服务器设置) 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到:SQL Server 网络配…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...
【iOS】 Block再学习
iOS Block再学习 文章目录 iOS Block再学习前言Block的三种类型__ NSGlobalBlock____ NSMallocBlock____ NSStackBlock__小结 Block底层分析Block的结构捕获自由变量捕获全局(静态)变量捕获静态变量__block修饰符forwarding指针 Block的copy时机block作为函数返回值将block赋给…...
