Python运维之多线程!!

一、多线程

二、多线程编程之threading模块

2.1、使用threading进行多线程操作有两种方法:

三、多线程同步之Lock(互斥锁)

四、多线程同步之Semaphore(信号量)

五、多线程同步之Condition

六、多线程同步之Event

七、线程优先级队列(queue)

八、多线程之线程池pool

九、总结


一、多线程

线程(Thread)也称轻量级进程,是操作系统能够运行调度的最小单位,被包含在进程中,是进程中的实际运作单位。

线程自身不拥有系统资源,只拥有一些在运行中必不可少的资源,但可与同属一个进程的其他线程共享所拥有的全部资源

一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行

  • 就绪状态指线程具备运行的所有状态,逻辑上可以运行,在等待处理机
  • 运行状态指线程占有处理机正在运行
  • 阻塞状态指线程在等待一个事件,逻辑上不可执行

尽管GIL(全局锁 )限制了CPython多线程在CPU密集型任务中的并行性,但Python的多线程在I/O密集型任务中依然能够发挥多核CPU的优势,通过在I/O等待期间执行其他任务来提升程序的运行效率。

实例1:计算密集型任务-多进程!!多进程!!多进程!!

 from multiprocessing import Processimport os,time​# 计算密集型任务def work():res = 0for i in range(100000000):res *= i​if __name__ == '__main__':l = []print("本机为",os.cpu_count(),"核 CPU")start = time.time()for i in range(4):p = Process(target=work)    # 多进程l.append(p)p.start()for p in l:p.join()stop = time.time()print("计算密集型任务,多进程耗时 %s" % (stop - start))'''本机为 8 核 CPU计算密集型任务,多进程耗时 5.117187976837158'''

实例1:计算密集型任务-多线程!!多线程!!多线程!!

 import os,timefrom threading import Thread​# 计算密集型任务def work():res = 0for i in range(100000000):res *= i​if __name__ == '__main__':l = []print("本机为",os.cpu_count(),"核 CPU")start = time.time()for i in range(4):p = Thread(target=work)    # 多线程l.append(p)p.start()for p in l:p.join()stop = time.time()print("计算密集型任务,多线程耗时 %s" % (stop - start))'''本机为 8 核 CPU计算密集型任务,多线程耗时 14.287675857543945'''

实例2:I/O密集型任务-多进程!!多进程!!多进程!!

 from multiprocessing import Processimport os,time​# I/O密集型任务def work():time.sleep(2)print("===>",file=open("tmp.txt","w"))​​if __name__ == '__main__':l = []print("本机为", os.cpu_count(), "核 CPU")start = time.time()for i in range(400):p = Process(target=work)  # 多进程l.append(p)p.start()for p in l:p.join()stop = time.time()print("I/O密集型任务,多进程耗时 %s" % (stop - start))'''本机为 8 核 CPUI/O密集型任务,多进程耗时 11.03010869026184'''

实例2:I/O密集型任务-多线程!!多线程!!多线程!!

 import os,timefrom threading import Thread​# I/O密集型任务def work():time.sleep(2)print("===>",file=open("tmp.txt","w"))​​if __name__ == '__main__':l = []print("本机为", os.cpu_count(), "核 CPU")start = time.time()for i in range(400):p = Thread(target=work)  # 多线程l.append(p)p.start()for p in l:p.join()stop = time.time()print("I/O密集型任务,多线程耗时 %s" % (stop - start))'''本机为 8 核 CPUI/O密集型任务,多线程耗时 2.0814177989959717'''

结论:在Python中,对于密集型任务,多进程占优势;对于I/O密集型任务,多线程占优势

二、多线程编程之threading模块

Python提供多线程编程的模块有两个:thread和threading。thread模块提供低级别的基本功能来支持,提供简单的锁来确保同步(不推荐)。threading模块对_thread进行了封装,提供了更高级别,功能更强。

2.1、使用threading进行多线程操作有两种方法:

方法一:创建threading.Thread类的实例调用其start()方法

 import timeimport threading​def task_thread(counter):print(f'线程名称:{threading.current_thread().name} 参数:{counter} 开始时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')num = counterwhile num:time.sleep(3)num -= 1print(f'线程名称:{threading.current_thread().name} 参数:{counter} 结束时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')​if __name__ == '__main__':print(f'主线程开始时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')​# 初始化三个线程,传递不同的参数t1 = threading.Thread(target=task_thread,args=(3,))t2 = threading.Thread(target=task_thread,args=(2,))t3 = threading.Thread(target=task_thread,args=(1,))# 开启三个线程t1.start();t2.start();t3.start()# 等待运行结束t1.join();t2.join();t3.join()​print(f'主线程结束时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')'''程序实例化了三个Thread类的实例,并任务函数传递不同的参数,使他们运行不同的时间后结束,start()方法开启线程,join()方法阻塞主线程,等待当前线程运行结束。'''

方法二:继承Thread类,在子类中重写run()和init()方法*(了解---)

 import timeimport threading​class MyThread(threading.Thread):def __init__(self,counter):super().__init__()self.counter = counter​def run(self):print(f'线程名称:{threading.current_thread().name} 参数:{self.counter} 开始时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')counter = self.counterwhile counter:time.sleep(3)counter -= 1print(f'线程名称:{threading.current_thread().name} 参数:{self.counter} 结束时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')if __name__ == '__main__':print(f'主线程开始时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')​# 初始化三个线程,传递不同的参数t1 = MyThread(3)t2 = MyThread(2)t3 = MyThread(1)# 开启三个线程t1.start();t2.start();t3.start()# 等待运行结束t1.join();t2.join();t3.join()​print(f'主线程结束时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')

如果继承Thread类,想要调用外部函数:

 import timeimport threading​​def task_thread(counter):print(f'线程名称:{threading.current_thread().name} 参数:{counter} 开始时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')num = counterwhile num:time.sleep(3)num -= 1print(f'线程名称:{threading.current_thread().name} 参数:{counter} 结束时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')​​class MyThread(threading.Thread):def __init__(self,target,args):super().__init__()self.args = argsself.target = target​def run(self):self.target(*self.args)​if __name__ == '__main__':print(f'主线程开始时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')​# 初始化三个线程,传递不同的参数t1 = MyThread(target=task_thread,args=(3,))t2 = MyThread(target=task_thread,args=(2,))t3 = MyThread(target=task_thread,args=(1,))# 开启三个线程t1.start();t2.start();t3.start()# 等待运行结束t1.join();t2.join();t3.join()​print(f'主线程结束时间:{time.strftime("%Y-%m-%d %H:%M:%S")}')

三、多线程同步之Lock(互斥锁)

如果多个线程共同对某个数据修改,则可能出现不可预料的结果,这个时候就需要使用互斥锁来进行同步。例如:在三个线程对共同变量num进行100万次加减操作后,其num的结果不为0

不加锁的意外情况:

 import time,threading​num = 0​def task_thread(n):global  numfor i in range(1000000):num = num + nnum = num - n​t1 = threading.Thread(target=task_thread,args=(6,))t2 = threading.Thread(target=task_thread,args=(17,))t3 = threading.Thread(target=task_thread,args=(11,))t1.start();t2.start();t3.start()t1.join();t2.join();t3.join()print(num)'''6'''

使用互斥锁对多个线程进行同步,限制当一个线程正在访问数据时,其他只能等待,直到前一线程释放锁。

使用threading.Thread对象的LockRlock可以实现简单的线程同步,都有acquirerelease方法。

 # 加互斥锁后运行结果始终一致import time,threading​num = 0lock = threading.Lock()def task_thread(n):global num# 获取锁,用于线程同步lock.acquire()for i in range(1000000):num = num + nnum = num - n# 释放锁,开启下一个线程lock.release()​​t1 = threading.Thread(target=task_thread,args=(6,))t2 = threading.Thread(target=task_thread,args=(17,))t3 = threading.Thread(target=task_thread,args=(11,))t1.start();t2.start();t3.start()t1.join();t2.join();t3.join()print(num)

四、多线程同步之Semaphore(信号量)

互斥锁是只允许一个线程访问共享数据,而信号量是同时运行一定数量的线程访问共享数据,比如银行柜台有5个窗口,运行同时有5个人办理业务,后面的人只能等待其完成。

 # 使用信号量控制并发import threadingimport time​# 银行柜台窗口数量NUM_WINDOWS = 5​# 用于控制窗口访问的信号量semaphore = threading.Semaphore(NUM_WINDOWS)​# 客户办理业务的函数def customer(name, service_time):# 尝试获取信号量semaphore.acquire()print(f"{time.ctime()}: {name} 开始办理业务")time.sleep(service_time)  # 模拟办理业务的时间print(f"{time.ctime()}: {name} 办理业务完成")semaphore.release()  # 释放信号量​# 创建客户线程列表customers = []for i in range(12):name = f"客户{i+1}"service_time = 3  # 假设每个客户办理业务需要1秒时间thread = threading.Thread(target=customer, args=(name, service_time))customers.append(thread)​# 启动所有客户线程for customer in customers:customer.start()​# 等待所有客户完成业务for customer in customers:customer.join()​print("所有客户都办理完业务了。")

上述代码实现了同一时刻只有5个线程获得资源运行

五、多线程同步之Condition

条件对象Condition能让一个线程A停下来,等待其他线程B,线程B满足了某个条件后通知线程A继续运行。步骤:

线程首先获取一个条件变量锁,如果条件不足,则该线程等待(wait)并释放条件变量锁;如果条件满足,就继续执行线程,执行完成后可以通知(notify)其他状态为wait的线程执行。其他处于wait状态的线程接到通知后会重新判断条件来确定是否继续执行

 import threading​class Boy(threading.Thread):def __init__(self,cond,name):super(Boy,self).__init__()self.cond = condself.name = name​def run(self):self.cond.acquire()print(self.name + ":嫁给我吧!?")self.cond.notify()  # 唤醒一个挂起的线程,让hanmeimei表态self.cond.wait()    # 释放内部所占用的锁,同时线程被挂起,直至接收到通知被唤醒或超时,等待heimeimei回答print(self.name + ": 我单膝下跪,送上戒指!")self.cond.notify()self.cond.wait()print(self.name + ": lI太太,你的选择太明智了。")self.cond.release()class Girl(threading.Thread):def __init__(self,cond,name):super(Girl,self).__init__()self.cond = condself.name = name​def run(self):self.cond.acquire()self.cond.wait()    # 等待Lilei求婚print(self.name + ": 没有情调,不够浪漫,不答应")self.cond.notify()self.cond.wait()print(self.name + ": 好吧,答应你了")self.cond.notify()self.cond.release()​cond = threading.Condition()boy = Boy(cond,"LiLei")girl = Girl(cond,"HanMeiMei")girl.start()boy.start()

六、多线程同步之Event

事件用于线程之间的通信。一个线程发出一个信号,其他一个或多个线程等待,调用Event对象的wait方法,线程则会阻塞等待,直到别的线程set之后才会被唤醒。与上述类似

 import threadingimport time​​class Boy(threading.Thread):def __init__(self,cond,name):super(Boy,self).__init__()self.cond = condself.name = name​def run(self):print(self.name + ":嫁给我吧!?")self.cond.set()  # 唤醒一个挂起的线程,让hanmeimei表态time.sleep(0.5)self.cond.wait()    # 释放内部所占用的锁,同时线程被挂起,直至接收到通知被唤醒或超时,等待heimeimei回答print(self.name + ": 我单膝下跪,送上戒指!")self.cond.set()time.sleep(0.5)self.cond.wait()self.cond.clear()print(self.name + ": lI太太,你的选择太明智了。")class Girl(threading.Thread):def __init__(self,cond,name):super(Girl,self).__init__()self.cond = condself.name = name​def run(self):self.cond.wait()    # 等待Lilei求婚self.cond.clear()print(self.name + ": 没有情调,不够浪漫,不答应")self.cond.set()time.sleep(0.5)self.cond.wait()print(self.name + ": 好吧,答应你了")self.cond.set()​cond = threading.Event()boy = Boy(cond,"LiLei")girl = Girl(cond,"HanMeiMei")girl.start()boy.start()

七、线程优先级队列(queue)

Python的queue模块中提供了同步的线程安全的队列类,包括先进先出队列Queue后进先出队列LifoQueue优先级队列PriorityQueue。这些队列都实现了锁原语,可以直接使用来实现线程之间的同步。

 '''有一小冰箱用来存放冷饮,假如只能放5瓶冷饮,A不停地放冷饮,B不停的取冷饮,A和B的放取速度不一致,如何保持同步呢?'''import threading,timeimport queue​# 先进先出q = queue.Queue(maxsize=5)# q = LifoQuere(maxsize=3)# q = PriorityQueue(maxsize=3)​def ProducerA():count = 1while True:q.put(f"冷饮 {count}")print(f"{time.strftime('%H:%M:%S')} A 放入:[冷饮 {count}]")count += 1time.sleep(2)​def ConsumerB():while True:print(f"{time.strftime('%H:%M:%S')} B 取出:{q.get()}")time.sleep(5)​p =  threading.Thread(target=ProducerA)c = threading.Thread(target=ConsumerB)p.start()c.start()

八、多线程之线程池pool

将 任务添加到线程池中,线程池会自动指定一个空闲的线程去执行任务,当超过线程池的最大线程数时,任务需要等待有新的空闲线程后才会被执行。

使用threading模块及queue模块定制线程池,可以使用multiprocessing。

  • from multiprocessing import Pool导入的是进程池

  • from multiprocessing.dummy import Pool导入的是线程池

 '''模拟一个耗时2秒的任务,比较其顺序执行5次和线程池(并发数为5)执行的耗时。'''from multiprocessing.dummy import Pool as ThreadPoolimport time​def fun(n):time.sleep(2)​start = time.time()for i in range(5):fun(i)print("单线程顺序执行耗时:",time.time() - start)​start2 = time.time()# 开8个worker,没有参数时默认是cpu的核心数pool = ThreadPool(processes=5)# 在线程中执行urllib2.urlopen(url)并返回执行结果results2 = pool.map(fun,range(5))pool.close()pool.join()print("线程池(5)并发执行耗时:",time.time() - start2)'''单线程顺序执行耗时: 10.041245937347412线程池(5)并发执行耗时: 2.0453202724456787'''

九、总结

  • Python多线程适合用在I/O密集型任务中。
  • 对于I/O密集型任务来说,较少时间用在CPU计算上,较多时间用在I/O上,如文件读写、web请求、数据库请求
  • 对于计算密集型任务,应该使用多进程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/9210.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CSS和JavaScript

CSS 在html中引入CSS 我们需要先在该项目先建立css文件 html引入CSS,在<head></head>中添加<link>标签 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" co…

mac 本地使用docker 运行es,kibana

1.下载 m芯片一些版本不支持.踩过坑.翻看官网才知道只有部分镜像支持m芯片 https://hub.docker.com/添加链接描述 docker pull elasticsearch:7.17.21 docker pull kibana:7.17.21镜像已经下载下来了 2.创建文件映射-挂载 /Users/lin/dev/dockerMsg 其中lin是自己的用户名…

关于线程池,它的扩展问题你知道吗?(自己总结)

专门想一下为什么线程池不用Excutors&#xff0c;之前的印象是错的&#xff0c;居然还拿来面试里讲&#xff0c;惭愧&#xff0c;这里暂时整理俩小问题&#xff0c;其他的后续可能会更新。。 线程池是创建的越大越好嘛 #线程池创建的越大越好吗 Tip&#xff1a;2024-04-10 更…

本地搭建hydra服务用go以验证oidc流程

目录 1、docker搭建hydra&#xff0c;环境配置&#xff1a; 2、搭建完成后服务调用&#xff1a; 2.1保证服务正常启动&#xff1a; 2.2 通过postman调用&#xff0c;获取client_id&#xff1a; 2.3 通过client_id&#xff0c;实现oauth2/auth调用 3. 通过go语言实现oidc验…

【qt】容器的用法

容器目录 一.QVertor1.应用场景2.增加数据3.删除数据4.修改数据5.查询数据6.是否包含7.数据个数8.交换数据9.移动数据10.嵌套使用 二.QList1.应用场景2.QStringList 三.QLinkedList1.应用场景2.特殊点3.用迭代器来变量 四.QStack1.应用场景2.基本用法 五.QQueue1.应用场景2.基本…

OS复习笔记ch5-3

引言 上一节我们学习了关于信号量机制的一些内容&#xff0c;包括信号量的含义&#xff0c;对应的PV操作等。 如图所示&#xff0c;上一节主要是针对信号量的互斥&#xff0c;其实信号量机制还可以做很多事情&#xff0c;比如实现进程同步和前驱关系&#xff0c;这一节我们先复…

【Spring】JdbcTemplate

JdbcTemplate 是 Spring 提供的一个 JDBC 模板类&#xff0c;是对 JDBC 的封装&#xff0c;简化 JDBC 代码 也可以让 Spring 集成其它的 ORM 框架&#xff0c;例如&#xff1a;MyBatis、Hibernate 等 使用 JdbcTemplate 完成增删改查 一、环境准备 数据库&#xff1a; 准备…

Marin说PCB之如何快速打印输出整板的丝印位号图?

当小编我辛辛苦苦加班加点的把手上的板子做到投板评审状态的时候&#xff0c;坐在我旁边的日本同事龟田小郎君说让我把板子上的丝印也要调一下&#xff0c;我当时就急了&#xff0c;这么大的板子&#xff0c;将近1W多PIN 了都&#xff0c;光调丝印都要老半天啊&#xff0c;而且…

Docx文件误删除如何恢复?别再花冤枉钱了,4个高效恢复软件!

不管是工作还是学习&#xff0c;总是会与各种各样的文件打交道。文件量越多就越容易出现文件丢失、文件误删的情况。遇到这些情况&#xff0c;失去的文件还能找回来吗&#xff1f;只要掌握了一些数据恢复方法&#xff0c;是很有机会恢复回来的&#xff0c;下面我会将这些方法分…

[机器学习系列]深入探索回归决策树:从参数选择到模型可视化

目录 一、回归决策树的参数 二、准备数据 三、构建回归决策树 (一)拟合模型 (二)预测数据 (三)查看特征重要性 (四)查看模型拟合效果 (五) 可视化回归决策树真实值和预测值 (六)可视化决策树并保存 部分结果如下&#xff1a; 一、回归决策树的参数 DecisionTreeRegress…

NVIDIA_SMI has failed because it couldn’t communicate with the NVIDIA driver

参考&#xff1a;https://www.zhihu.com/question/474222642/answer/3127013936 https://blog.csdn.net/ZhouDevin/article/details/128265656 nvidia-smi查看报错&#xff0c;nvcc正常 1&#xff09;查看nvidia版本 ls /usr/src | grep nvidia nvidia-550.78 2&#xff09;…

暗区突围国际服pc端怎么获取测试资格 twitch掉落资格获取教程

《暗区突围》是由腾讯魔方工作室群开发的第一人称射击类手游。游戏以从暗区撤离并收集物资满载而归作为最终目的&#xff0c;带出的战利品可以存储在仓库中&#xff0c;又可以出售用以换取游戏金钱。游戏中玩家可以创建男性或女性角色&#xff0c;可以通过选择脸型、发型、发色…

C++ 动态内存管理

例如&#xff1a;动态内存和释放单个数据的存储区 一 用new运算符初始化单个数据的存储区 举例

【智能算法】人工原生动物优化算法(APO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.获取代码 1.背景 2024年&#xff0c;X Wang受到自然界原生动物启发&#xff0c;提出了人工原生动物优化算法&#xff08; Artificial Protozoa Optimizer, APO&#xff09;。 2.算法原理 2.1算法思想 AP…

压缩归档库-Snappy介绍

1.简介 Snappy 是一个 C 编写的压缩和解压缩库&#xff0c;由 Google 开发。它专为速度而设计&#xff0c;而不是最大压缩率或与其他压缩库的兼容性。 Snappy 通常用于需要快速压缩和解压缩的场景。 Snappy具有以下属性&#xff1a; 快速&#xff1a;压缩速度达到250 MB/秒及…

数智化快速开发平台

助力企业IT规划标准化&#xff0c;实现企业IT生态化 目前市场上有很多面向企业各种业务场景的产品&#xff0c;这些产品给企业管理带来便利性的同时&#xff0c;也带来了一系列问题&#xff0c;例如&#xff1a; 不同系统的后台管理功能基本一致&#xff0c;却需要重复建设&a…

linux调试

文章目录 1. 使用打印来调试1.1 重定向1.2 标准预定义宏1.3 日志代码 2. 内核异常2.1 内核打印2.1.1 打印级别2.1.2 跟踪异常2.1.3 动态打印2.1.4 RAM console 2.2 OOPS2.2.1 有源代码的情况2.2.2 没有源代码的情况 3 查看日志4 工具调试 1. 使用打印来调试 1.1 重定向 2>…

[Collection与数据结构] Map与Set(一):二叉搜索树与Map,Set的使用

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏:&#x1f355; Collection与数据结构 (91平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 &#x1f9c0;Java …

python socket通讯 学习记录

python socket 1. 初级实现2. 添加header3. 中级实现&#xff08;引用pickle库&#xff09;4. 高级实现&#xff08;相互发送信息&#xff09;5. 一点尝试5. 1个server对应2个client5.2个server对应1个client 名称版本python3.11 本文涉及到socket的server与client通讯从简单到…

LeetCode 257. 二叉树的所有路径

LeetCode 257. 二叉树的所有路径 1、题目 题目链接&#xff1a;257. 二叉树的所有路径 给你一个二叉树的根节点 root &#xff0c;按 任意顺序 &#xff0c;返回所有从根节点到叶子节点的路径。 叶子节点 是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root…