python多线程使用方法实例详解
本文实例讲述了python多线程使用方法。分享给大家供大家参考,具体如下:
threading模块支持守护线程,其工作方式是:守护线程一般是一个等待客户端请求服务的服务器。
如果把一个线程设置为守护线程,进程退出时不需要等待这个线程执行完成。
如果主线程准备退出时,不需要等待某些子线程完成,就可以为这些子线程设置守护线程标记。需要在启动线程之前执行如下赋值语句:thread.daemon=True,检查线程的守护状态也只需要检查这个值即可。
整个Python程序将在所有非守护线程退出之后才退出,换句话说,就是没有剩下存活的非守护线程时才退出。
使用thread模块
以下是三种使用Thread类的方法(一般使用第一个或第三个方案)
- 创建Thread的实例,传给它一个函数。
importthreading fromtimeimportsleep,ctime loops=[3,2,1,1,1] defloop(i,nsec): print(f'startloop{i}at:{ctime()}') sleep(nsec) print(f'endloop{i}at:{ctime()}') defmain(): print('startat',ctime()) threads=[] nloops=range(len(loops)) foriinnloops: t=threading.Thread(target=loop,args=(i,loops[i])) threads.append(t) foriinnloops:#startthreads threads[i].start() foriinnloops:#waitforall threads[i].join()#threadstofinish print(f'alldoneat:{ctime()}') if__name__=='__main__': main()
当所有线程都分配完成之后,通过调用每个线程的start()方法让它们开始执行,而不是在这之前就会执行。
相比于管理一组锁(分配、获取、释放、检查锁状态等)而言,这里只需要为每个线程调用join()方法即可。
join()方法将等待线程结束,或者在提供了超时时间的情况下,达到超时时间。
使用join()方法要比等待锁释放的无限循环更加清晰(这也是这种锁又称为自旋锁的原因)。
- 创建Thread的实例,传给它一个可调用的类实例。
importthreading fromtimeimportsleep,ctime #创建Thread的实例,传给它一个可调用的类实例 loops=[3,2,1,1,1] classThreadFunc(object): def__init__(self,func,args,name=''): self.name=name self.func=func self.args=args def__call__(self): self.func(*self.args) defloop(i,nsec): print(f'startloop{i}at:{ctime()}') sleep(nsec) print(f'endloop{i}at:{ctime()}') defmain(): print('startat',ctime()) threads=[] nloops=range(len(loops)) foriinnloops: t=threading.Thread(target=ThreadFunc(loop,(i,loops[i]),loop.__name__)) threads.append(t) foriinnloops:#startthreads threads[i].start() foriinnloops:#waitforall threads[i].join()#threadstofinish print(f'alldoneat:{ctime()}') if__name__=='__main__': main()
- 派生Thread的子类,并创建子类的实例。
importthreading fromtimeimportsleep,ctime #创建Thread的实例,传给它一个可调用的类实例 #子类的构造函数必须先调用其基类的构造函数 #特殊方法__call__()在子类中必须要写为run() loops=[3,2,1,1,1] classMyThread(threading.Thread): def__init__(self,func,args,name=''): threading.Thread.__init__(self) self.name=name self.func=func self.args=args defrun(self): self.func(*self.args) defloop(i,nsec): print(f'startloop{i}at:{ctime()}') sleep(nsec) print(f'endloop{i}at:{ctime()}') defmain(): print('startat',ctime()) threads=[] nloops=range(len(loops)) foriinnloops: t=MyThread(loop,(i,loops[i]),loop.__name__) threads.append(t) foriinnloops:#startthreads threads[i].start() foriinnloops:#waitforall threads[i].join()#threadstofinish print(f'alldoneat:{ctime()}') if__name__=='__main__': main()
使用锁
python和java一样,也具有锁机制,而且创建与使用锁都是很简便的。
一般在多线程代码中,总会有一些特定的函数或代码块不希望(或不应该)被多个线程同时执行,通常包括修改数据库、更新文件或其他会产生竞态条件的类似情况
锁有两种状态:锁定和未锁定。而且它也只支持两个函数:获得锁和释放锁。
一般锁的调用如下
#加载线程的锁对象 lock=threading.Lock() #获取锁 lock.acquire() #...代码 #释放锁 lock.release()
更简洁的方法是使用with关键字,如下代码功能同上
#加载线程的锁对象 lock=threading.Lock() withlock: #...代码
示例代码:
importthreading fromtimeimportsleep,ctime lock=threading.Lock() defa(): lock.acquire() forxinrange(5): print(f'a:{str(x)}') sleep(0.01) lock.release() defb(): lock.acquire() forxinrange(5): print(f'a:{str(x)}') sleep(0.01) lock.release() threading.Thread(target=a).start() threading.Thread(target=b).start()
相关属性和方法
- Thread对象的属性
属性 | 描述 |
---|---|
name | 线程名 |
ident | 线程的标识符 |
daemon | 布尔标志,表示这个线程是否是守护线程 |
- Thread对象的方法
方法 | 描述 |
---|---|
init(group=None,tatget=None,name=None,args=(),kwargs={},verbose=None,daemon=None) | 实例化一个线程对象,需要有一个可调用的target,以及其参数args或kwargs。还可以传递name或group参数,不过后者还未实现。此外,verbose标志也是可接受的。而daemon的值将会设定thread.daemon属性/标志 |
start() | 开始执行该线程 |
run() | 定义线程功能的方法(通常在子类中被应用开发者重写) |
join(timeout=None) | 直至启动的线程终止之前一直挂起;除非给出了timeout(秒),否则会一直阻塞 |
is_alive() | 布尔标志,表示这个线程是否还存活 |
- threading模块其他函数
函数 | 描述 |
---|---|
start() | 开始执行该线程 |
active_count() | 当前活动的Thread对象个数 |
enumerate() | 返回当前活动的Thread对象列表 |
settrace(func) | 为所有线程设置一个trace函数 |
setprofile(func) | 为所有线程设置一个profile函数 |
stack_size(size=0) | 返回新创建线程的栈大小;或为后续创建的线程设定栈的大小为size |
Lock() | 加载线程的锁对象,是一个基本的锁对象,一次只能一个锁定,其余锁请求,需等待锁释放后才能获取,对象有acquire()和release()方法 |
RLock() | 多重锁,在同一线程中可用被多次acquire。如果使用RLock,那么acquire和release必须成对出现,调用了n次acquire锁请求,则必须调用n次的release才能在线程中释放锁对象 |
后记
在Python多线程下,每个线程的执行方式:
1、获取GIL
2、执行代码直到sleep或者是python虚拟机将其挂起。
3、释放GIL
通常来说,多线程是一个好东西。不过由于Python的GIL的限制,多线程更适合于I/O密集型应用(I/O释放了GIL,可以允许更多的并发),而不是计算密集型应用。对于后一种情况而言,为了实现更好的并行性,你需要使用多进程,以便让CPU的其他内核来执行。
请注意:多核多线程比单核多线程更差,原因是单核下多线程,每次释放GIL,唤醒的那个线程都能获取到GIL锁,所以能够无缝执行,但多核下,CPU0释放GIL后,其他CPU上的线程都会进行竞争,但GIL可能会马上又被CPU0拿到,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低
更多关于Python相关内容感兴趣的读者可查看本站专题:《Python进程与线程操作技巧总结》、《Python数据结构与算法教程》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》、《Python入门与进阶经典教程》、《Python+MySQL数据库程序设计入门教程》及《Python常见数据库操作技巧汇总》
希望本文所述对大家Python程序设计有所帮助。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。