找回密码
 会员注册
查看: 35|回复: 0

Python超详细实例讲解python多线程(threading模块)

[复制链接]

3

主题

0

回帖

10

积分

新手上路

积分
10
发表于 2024-9-9 22:32:42 | 显示全部楼层 |阅读模式
什么是多线程?线程(thread)是操作系统中能够进行运算的最小单位,包含于进程之中,一个进程可以有多个线程,这意味着一个进程中可以并发多个线程,即为多线程。对于一个python程序,如果需要同时大量处理多个任务,有使用多进程和多线程两种方法。在python中,实现多线程主要通过threading模块,而多进程主要通过multiprocessing模块。这两个模块的主要区别是:threading模块基于线程,而multiprocessing模块基于进程。threading模块使用共享内存来实现多线程,所有线程都共享一样的变量(这点在后续的实例中可以感受到);而multiprocessing基于子进程,每个子进程之间都有独立的变量和数据结构。两者的区别意味着threading更使用于I/O密集型任务(例如需要进行多表格读取操作),multiprocessing模块更适用于包含较多计算的CPU密集型任务(矩阵运算,图片处理类任务)。需要注意的是,由于python中的GIL锁的存在,Python解释器只允许一个Python进程使用,这意味着对于一个解释器只允许一个进程在运行,这也是为什么threading模块无法适用于CPU密集型这类需要大量CPU资源的任务,因为一个进程的CPU资源有限,无论开启多少个线程,总的资源就只有那些,总耗时不会有太大变化。而multiprocessing模块则可以开多个进程,能够更快速的处理CPU密集型任务。关于GIL锁和Multiprocessing模块的部分就不继续深入介绍了,本次主要介绍如何使用threading模块实现多线程的相关内容。线程完整生命周期一个线程完整的生命周期包括新建——就绪——运行——阻塞——死亡。新建:即新创建一个线程对象就绪:调用start方法后,线程对象等待运行,什么时候开始运行取决于调度运行:线程处于运行状态阻塞:处于运行状态的线程被堵塞,通俗理解就是被卡住了,可能的原因包括但不限于程序自身调用sleep方法阻塞线程运行,或调用了一个阻塞式I/O方法,被阻塞的进程会等待何时解除阻塞重新运行死亡:线程执行完毕或异常退出,线程对象被销毁并释放内存主线程与子线程 我们讲的多线程实际上指的就是只在主线程中运行多个子线程,而主线程就是我们的python编译器执行的线程,所有子线程和主线程都同属于一个进程。在未添加子线程的情况下,默认就只有一个主线程在运行,他会将我们写的代码从开头到结尾执行一遍,后文中我们也会提到一些主线程与子线程的关系。不扯那么多概念了,接下来直接进入正题!实例1:直接使用Thread创建线程对象Thread类创建新线程的基本语法如下:Newthread =Thread(target=function,args=(argument1,argument2,...))Newthread:创建的线程对象function:要执行的函数argument1,argument2:传递给线程函数的参数,为tuple类型假设一个任务task(当然task可以替换为其他任何任务,本实例中仅为假设),这个任务实现的功能是每隔1s打印某个字母,我们使用两个子线程,分别同时打印不同的字母a和b,实例如下:"""Date:2024/5/15Author:猫猫不吃sakana"""fromthreadingimportThreadimporttimefromtimeimportsleep#自定义的函数,可以替换成其他任何函数deftask(threadName,number,letter):print(f"【线程开始】{threadName}")m=0whilemDate:2024/5/15Author:猫猫不吃sakana"""fromthreadingimportThreadimporttimefromtimeimportsleep#自定义的函数,可以替换成其他任何函数deftask(threadName,number,letter):print(f"【线程开始】{threadName}")m=0whilemDate:2024/5/15Author:猫猫不吃sakana"""importthreadingimporttimefromtimeimportsleep#myThread继承父类,并进行重写classmyThread(threading.Thread):#重写父类的构造函数def__init__(self,number,letter):threading.Thread.__init__(self)self.number=number#添加number变量self.letter=letter#添加letter变量#重写父类中的run函数defrun(self):print(f"【线程开始】{self.name}")task1(self.name,self.number,self.letter)print("【线程结束】",self.name)#重写父类析构函数def__del__(self):print("【线程销毁释放内存】",self.name)#自定义的函数,此处可以替换成任何其他想要多线程执行的任务deftask1(threadName,number,letter):m=0whilemDate:2024/5/15Author:猫猫不吃sakana"""importthreadingimporttimefromtimeimportsleep#myThread继承父类,并进行重写classmyThread(threading.Thread):#重写父类的构造函数def__init__(self,number,letter):threading.Thread.__init__(self)self.number=number#添加number变量self.letter=letter#添加letter变量self.daemon=True#默认前台线程#重写父类中的run函数defrun(self):print(f"【线程开始】{self.name}")task1(self.name,self.number,self.letter)print("【线程结束】",self.name)#重写父类析构函数def__del__(self):print("【线程销毁释放内存】",self.name)#自定义的函数,此处可以替换成任何其他想要多线程执行的任务deftask1(threadName,number,letter):m=0whilemDate:2024/5/15Author:猫猫不吃sakana"""importthreadingimporttime#子类myThread继承父类threading.Thread,并进行重写classmyThread(threading.Thread):#重写父类构造函数def__init__(self,number):threading.Thread.__init__(self)self.number=number#重写父类run函数,在调用start()时自动调用run函数defrun(self):print(f"【线程开始】{self.name}")Lock.acquire()#设置线程锁edit_list(self.name,self.number)Lock.release()#释放线程锁#重写父类析构函数def__del__(self):print("【线程销毁】",self.name)#自定义的任务函数defedit_list(threadName,number):whilenumber>0:time.sleep(1)data_list[number-1]+=1current_time=time.strftime('%H:%M:%S',time.localtime())print(f"[{current_time}]{threadName}修改datalist为{data_list}")number-=1print(f"【线程{threadName}完成工作】")data_list=[0,0,0,0]Lock=threading.Lock()#创建3个子线程thread1=myThread(1)thread2=myThread(2)thread3=myThread(3)#启动3个子线程thread1.start()thread2.start()thread3.start()#主进程等待所有线程完成thread1.join()thread2.join()thread3.join()print("【主进程结束】")输出为:【线程开始】Thread-1【线程开始】Thread-2【线程开始】Thread-3[09:55:22]Thread-1修改datalist为[1,0,0,0]【线程Thread-1完成工作】[09:55:23]Thread-2修改datalist为[1,1,0,0][09:55:24]Thread-2修改datalist为[2,1,0,0]【线程Thread-2完成工作】[09:55:25]Thread-3修改datalist为[2,1,1,0][09:55:26]Thread-3修改datalist为[2,2,1,0][09:55:27]Thread-3修改datalist为[3,2,1,0]【线程Thread-3完成工作】【主进程结束】【线程销毁】Thread-1【线程销毁】Thread-2【线程销毁】Thread-3 当三个线程都需要使用同一个数据时,我们只需要对线程的run方法中进行加锁和释放锁的操作即可。此时三个子线程将会进行顺序操作,前一个子线程执行完成释放锁后,后一个线程才会继续执行。要注意的是,这三个子线程使用的需要是同一把锁。 写在最后这是我第一次尝试发文,写文章的初衷就是和大家能够一起进步,有什么不足大家也可以在评论区提出来,后续可能会继续更新python的一些知识点以及自己的学习感悟,与各位共勉。threading模块还有很多可选参数和方法可供使用,详情可参见threading模块的官方文档点击链接:threading---Thread-basedparallelism—Python3.12.3文档https://docs.python.org/zh-cn/3/library/threading.html#
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-9 05:54 , Processed in 0.522726 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表