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万…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
