Python获取多线程返回结果
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Python获取多线程返回结果
本⽂转⾃,该页可在线运⾏以下实例
Python的thread模块,是不⽀持守护线程的。
当主线程退出的时候,所有的⼦线程都将终⽌,不管他们是否仍在⼯作。
本节我们开始介绍python的另外多线程模块threading,该模块⽀持守护线程,其⼯作⽅式:守护线程⼀般是⼀个等待客户端请求的服务器。
如果没有客户端请求,守护线程就是空闲的。
如果把⼀个线程设置为守护线程,就表⽰这个线程是不重要的,进程退出时不需要等待这个线程执⾏完成。
如果主线程准备退出的时候,不需要等待某些⼦线程完成,就可以为这些⼦线程设置守护线程标记。
该标记值为真的时候,标⽰线程是不重要的,或者说该线程只是⽤来等待客户端请求⽽不做其他任何事情。
使⽤下⾯的语句:thread.daemon=True 可以将⼀个线程设置为守护线程。
同样的也可以通过这个值来查看线程的守护状态。
⼀个新的⼦线程会继承⽗线程的守护标记。
整个python程序(也可以称作主线程)将在所有的⾮守护线程退出之后才退出。
threading模块除了Thread类之外,还包括许多好⽤的同步机制:
对象描述
Thread表⽰⼀个执⾏线程的对象
Lock锁对象
RLock可重⼊锁对象,使单⼀线程可以(再次)获得已持有的锁(递归锁)
Condition条件变量对象,使得⼀个线程等待另外⼀个线程满⾜特定的条件,⽐如改变状态或者某个数据值
Event条件变量的通⽤版本,任意数量的线程等待某个事件的发⽣,在该事件发⽣后所有的线程都将被激活
Semaphore为线程间的有限资源提供⼀个计数器,如果没有可⽤资源时会被阻塞
BoundedSemaphore于Semaphore相似,不过它不允许超过初始值
Timer于Thread类似,不过它要在运⾏前等待⼀定时间
Barrier创建⼀个障碍,必须达到指定数量的线程后才可以继续
其中,Thread类是threading模块的主要执⾏对象。
下⾯是Thread类的属性和⽅法列表:
属性描述
Thread类属性
name线程名
ident线程的标识符
daemon布尔值,表⽰这个线程是否是守护线程
Thread类⽅法
init(group=None,target=None,name=None,args= (),kwargs={},verbose=None,daemon=None)实例化⼀个线程对象,需要⼀个可调⽤的target对象,以及参数args或者kwargs。
还可以传递name和group参数。
daemon的值将会设定thread.daemon的属性
start()开始执⾏该线程
run()定义线程的⽅法。
(通常开发者应该在⼦类中重写)
join(timeout=None)直⾄启动的线程终⽌之前⼀直挂起;除⾮给出了timeout(单位秒),否则⼀直被阻塞
join(timeout=None)直⾄启动的线程终⽌之前⼀直挂起;除⾮给出了timeout(单位秒),否则⼀直被阻塞属性描述
getName()返回线程名(该⽅法已被弃⽤)
setName()设定线程名(该⽅法已弃⽤)
isAlive布尔值,表⽰这个线程是否还存活(驼峰式命名,python2.6版本开始已被取代)isDaemon()布尔值,表⽰是否是守护线程(已经弃⽤)
setDaemon(布尔值)在线程start()之前调⽤,把线程的守护标识设定为指定的布尔值(已弃⽤)
使⽤Thread类,可以有多种⽅法创建线程:
创建Thread类的实例,传递⼀个函数
创建Thread类的实例,传递⼀个可调⽤的类实例
派⽣Thread类的⼦类,并创建⼦类的实例
⼀般的,我们会采⽤第⼀种或者第三种⽅法。
如果需要⼀个更加符合⾯向对象的接⼝时,倾向于选择第三种⽅法,否则就⽤第⼀种⽅法吧。
第⼀种⽅法:创建Thread类,传递⼀个函数
下⾯的脚本中,我们先实例化Thread类,并传递⼀个函数(及其参数),当线程执⾏的时候,函数也会被执⾏:
import threading
from time import sleep,ctime
#不再把4秒和2秒硬性的编码到不同的函数中,⽽是使⽤唯⼀的loop()函数,并把这些常量放进列表loops中
loops=[4,2]
def loop(nloop,nsec):
print('开始循环',nloop,'at:',ctime())
sleep(nsec)
print('循环',nloop,'结束于:',ctime())
def main():
print('程序开始于:',ctime())
threads=[]
nloops=range(len(loops))
for i in nloops:
t=threading.Thread(target=loop,args=(i,loops[i])) #循环实例化2个Thread类,传递函数及其参数,并将线程对象放⼊⼀个列表中
threads.append(t)
for i in nloops:
threads[i].start() #循环开始线程
for i in nloops:
threads[i].join() #循环 join()⽅法可以让主线程等待所有的线程都执⾏完毕。
print('任务完成于:',ctime())
main()
和thread模块相⽐,不同点在于:实现同样的效果,thread模块需要锁对象,⽽threading模块的Thread类不需要。
实例化Thread(调⽤Thread())和调⽤thread.start_new_thread()的最⼤区别就是新线程不会⽴即执⾏!这是⼀个⾮常有⽤的同步功能,尤其在我们不希望线程⽴即开始执⾏的时候。
当所有的线程都分配完成之后,通过调⽤每个线程的start()⽅法再让他们开始。
相⽐于thread模块的管理⼀组锁(分配、获取、释放检查锁状态)来说,threading模块的Thread类只需要为每个线程调⽤join()⽅法即可。
join(timeout=None)⽅法将等待线程结束,或者是达到指定的timeout时间时。
这种锁⼜称为⾃旋锁。
最重要的是:join()⽅法,其实你根本不需要调⽤它。
⼀旦线程启动,就会⼀直执⾏,直到给定的函数完成后退出。
如果主线程还有其他事情要做(并不需要等待这些线程完成),可以不调⽤join()。
join()只有在你需要等待线程完成时候才是有⽤的。
例如上⾯的脚本中,我们注释掉join()代码:
import threading
from time import sleep,ctime
#不再把4秒和2秒硬性的编码到不同的函数中,⽽是使⽤唯⼀的loop()函数,并把这些常量放进列表loops中
loops=[4,2]
def loop(nloop,nsec):
print('开始循环',nloop,'at:',ctime())
sleep(nsec)
print('循环',nloop,'结束于:',ctime())
def main():
print('程序开始于:',ctime())
threads=[]
nloops=range(len(loops))
for i in nloops:
t=threading.Thread(target=loop,args=(i,loops[i])) #循环实例化2个Thread类,传递函数及其参数,并将线程对象放⼊⼀个列表中
threads.append(t)
for i in nloops:
threads[i].start() #循环开始线程
#for i in nloops:
#threads[i].join() #循环 join()⽅法可以让主线程等待所有的线程都执⾏完毕。
print('任务完成于:',ctime())
main()
我们发现:主线程的任务⽐两个循环线程要先执⾏(任务完成于……在循环X结束……的前⾯)
第⼆种⽅法:创建Thread类的实例,传递⼀个可调⽤的类实例
创建线程时,于传⼊函数类似的⽅法是传⼊⼀个可调⽤的类的实例,⽤于线程执⾏——这种⽅法更加接近⾯向对象的多线程编程。
⽐起⼀个函数或者从⼀个函数组中选择⽽⾔,这种可调⽤的类包含⼀个执⾏环境,有更好的灵活性。
在上述的mtsleepC.py脚本中添加⼀个新类ThreadFunc,稍微改动⼀番,形成mtsleepD.py⽂件:
#!/usr/bin/env python
import threading
from time import sleep,ctime
loops=[4,2]
class ThreadFunc(object):
def __init__(self,func,args,name=''):
=name
self.func = func
self.args=args
def __call__(self):
self.func(*self.args)
def loop(nloop,nsec):
print('开始循环',nloop,'在:',ctime())
sleep(nsec)
print('结束循环',nloop,'于:',ctime())
def main():
print('程序开始于:',ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
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())
main()
上述脚本中,主要添加了ThreadFunc类,并在实例化Thread对象时,通过传参的形式同时实例化了可调⽤类ThreadFunc。
这⾥同时完成了两个实例化。
我们研究⼀下创建ThreadFunc类的思想:我们希望这个类更加通⽤,⽽不是局限于loop()函数,为此,添加了⼀些新的东西,⽐如这个类保存了函数⾃⾝、函数的参数、以及函数名。
构造函数__init__()⽤于设定上述值。
当创建新线程的时候,Thread类的代码将调⽤ThreadFunc 对象,此时会调⽤__call__()这个特殊⽅法。
(⽼实说,这种⽅法显得有些尴尬,并且稍微难以阅读)
第三种⽅法:派⽣Thread的⼦类,并创建⼦类的实例
和⽅法⼆相⽐,⽅法三再创建线程时使⽤⼦类要相对更容易阅读,下⾯是mtsleepE.py脚本:
#!/usr/bin/env pyhton
import threading
from time import sleep,ctime
class MyThread(threading.Thread):
def __init__(self,func,args,name=''):
threading.Thread.__init__(self)
= name
self.func = func
self.args = args
def run(self):
self.func(*self.args)
def loop(nloop,nsec):
print('开始循环',nloop,'在:',ctime())
sleep(nsec)
print('结束循环',nloop,'于:',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())
main()
⽐较⽅法⼆和⽅法三,重要的变化在于:MyThread⼦类的构造函数必须先调⽤其⽗类的构造函数;重写run()⽅法,代替⽅法⼆中的
__call__()⽅法。
优化第三种⽅法:
对MyThread类进⾏修改,增加⼀些调试信息的输出,另外,除了简单的调⽤函数外,还可以将结果保存在实例的属性self.res中,同时增加新的⽅法getResult()来获取这个值:
以下代码⽐较了递归求斐波那契、阶乘和累计函数的执⾏,分别按照单线程和多线程的⽅式执⾏同样的任务:
#!/usr/bin/env python
import threading
from time import ctime,sleep
#对MyThread进⾏封装
class MyThread(threading.Thread):
def __init__(self,func,args,name=''):
threading.Thread.__init__(self)
self.func = func
= name
self.args = args
def run(self):
print('开始执⾏',,' 在:',ctime())
self.res = self.func(*self.args)
print(,'结束于:',ctime())
def getResult(self):
return self.res
#斐波那契
def fib(x):
sleep(0.005)
if x < 2:
return 1
return fib(x-1)+fib(x-2)
#阶乘
def fac(x):
sleep(0.1)
if x < 2:
return 1
return x*fac(x-1)
#累加
def sum(x):
sleep(0.1)
if x < 2 :
return x + sum(x-1)
funcs=[fib,fac,sum]
n = 12
def main():
nfuncs = range(len(funcs))
#单线程
print('单线程模式')
for i in nfuncs:
print('开始',funcs[i].__name__,' 在:',ctime())
print(funcs[i](n))
print(funcs[i].__name__,'结束于:',ctime())
#多线程
print('多线程模式')
threads = []
for i in nfuncs :
t = MyThread(funcs[i],(n,),funcs[i].__name__)
threads.append(t)
for i in nfuncs:
threads[i].start()
for i in nfuncs:
threads[i].join()
print(threads[i].getResult())
print('所有的任务结束')
main()
总结
程序中,为了看到多线程如何改善性能的,我们加⼊了sleep函数⽤于减慢执⾏速度。
看到单线程模式中,只是简单的⼀次调⽤每个函数,并在函数结束执⾏的时候⽴即显⽰相关的结果;⽽使⽤多线程的时候,并不会⽴刻显⽰结果,因为我们希望MyThread类越通⽤越好(有输出和⽆输出都能执⾏),我们⼀直等到所有线程都join之后,再调⽤getResult()⽅法显⽰每个函数的返回值。