python3多线程第三方库_Python3标准库:concurrent.futures管理并发任务池

Python

Python开发

Python语言

Python3标准库:concurrent.futures管理并发任务池

1. concurrent.futures管理并发任务池

concurrent.futures模块提供了使用工作线程或进程池运行任务的接口。线程和进程池的API是一样的,所以应用只做最小的修改就可以在线程和进程之间顺利地切换。

这个模块提供了两种类型的类与这些池交互。执行器(executor)用来管理工作线程或进程池,future用来管理工作线程或进程计算的结果。要使用一个工作线程或进程池,应用要创建适当的执行器类的一个实例,然后向它提交任务来运行。每个任务启动时,会返回一个Future实例。需要任务的结果时,应用可以使用Future阻塞,直到得到结果。目前已经提供了不同的API,可以很方便地等待任务完成,所以不需要直接管理Future对象。

1.1 利用基本线程池使用map()

ThreadPooLExecutor管理一组工作线程,当这些线程可用于完成更多工作时,可以向它们传入任务。下面的例子使用map()并发地从一个输入迭代器生成一组结果。这个任务使用time.sleep()暂停不同的时间,从而展示不论任务的执行顺序如何,map()总是根据输入按顺序返回值。

from concurrent importfuturesimportthreadingimporttimedeftask(n):print('{}: sleeping {}'.format(

threading.current_thread().name,

n)

)

time.sleep(n/ 10)print('{}: done with {}'.format(

threading.current_thread().name,

n)

)return n / 10ex= futures.ThreadPoolExecutor(max_workers=2)print('main: starting')

results= ex.map(task, range(5, 0, -1))print('main: unprocessed results {}'.format(results))print('main: waiting for real results')

real_results=list(results)print('main: results: {}'.format(real_results))

map()的返回值实际上是一种特殊类型的迭代器,它知道主程序迭代处理时要等待各个响应。

1.2 调度单个任务

除了使用map(),还可以借助submit()利用一个执行器调度单个任务。然后可以使用返回的Future实例等待这个任务的结果。

from concurrent importfuturesimportthreadingimporttimedeftask(n):print('{}: sleeping {}'.format(

threading.current_thread().name,

n)

)

time.sleep(n/ 10)print('{}: done with {}'.format(

threading.current_thread().name,

n)

)return n / 10ex= futures.ThreadPoolExecutor(max_workers=2)print('main: starting')

f= ex.submit(task, 5)print('main: future: {}'.format(f))print('main: waiting for results')

result=f.result()print('main: result: {}'.format(result))print('main: future after result: {}'.format(f))

任务完成之后,Future的状态会改变,并得到结果。

1.3 按任意顺序等待任务

调用Future的result()方法会阻塞,直到任务完成(可能返回一个值,也可能抛出一个异常)或者撤销。可以使用map()按调度任务的顺序访问多个任务的结果。如果处理结果的顺序不重要,则可以使用as_completed()在每个任务完成时处理它的结果。

from concurrent importfuturesimportrandomimporttimedeftask(n):

time.sleep(random.random())return (n, n / 10)

ex= futures.ThreadPoolExecutor(max_workers=5)print('main: starting')

wait_for=[

ex.submit(task, i)for i in range(5, 0, -1)

]for f infutures.as_completed(wait_for):print('main: result: {}'.format(f.result()))

因为池中的工作线程与任务同样多,故而所有任务都可以启动。它们会按随机的顺序完成,所以每次运行这个示例程序时as_completed()生成的值都不同。

1.4 回调

要在任务完成时采取某个动作,不用显式地等待结果,可以使用add_done_callback()指示Future完成时要调用一个新函数。这个回调应当是有一个参数(Future实例)的callable函数。

from concurrent importfuturesimporttimedeftask(n):print('{}: sleeping'.format(n))

time.sleep(0.5)print('{}: done'.format(n))return n / 10

defdone(fn):iffn.cancelled():print('{}: canceled'.format(fn.arg))eliffn.done():

error=fn.exception()iferror:print('{}: error returned: {}'.format(

fn.arg, error))else:

result=fn.result()print('{}: value returned: {}'.format(

fn.arg, result))if __name__ == '__main__':

ex= futures.ThreadPoolExecutor(max_workers=2)print('main: starting')

f= ex.submit(task, 5)

f.arg= 5f.add_done_callback(done)

result= f.result()

不论由于什么原因,只要认为Future“完成”,就会调用这个回调,所以在使用它之前必须检查传入回调的对象的状态。

1.5 撤销任务

如果一个Future已经提交但还没启动,那么可以调用它的cancel()方法将其撤销。

from concurrent importfuturesimporttimedeftask(n):print('{}: sleeping'.format(n))

time.sleep(0.5)print('{}: done'.format(n))return n / 10

defdone(fn):iffn.cancelled():print('{}: canceled'.format(fn.arg))eliffn.done():print('{}: not canceled'.format(fn.arg))if __name__ == '__main__':

ex= futures.ThreadPoolExecutor(max_workers=2)print('main: starting')

tasks=[]for i in range(10, 0, -1):print('main: submitting {}'.format(i))

f=ex.submit(task, i)

f.arg=i

f.add_done_callback(done)

tasks.append((i, f))for i, t inreversed(tasks):if nott.cancel():print('main: did not cancel {}'.format(i))

ex.shutdown()

cancel()返回一个布尔值,指示任务是否可用撤销。

1.6 任务中的异常

如果一个任务产生一个未处理的异常,那么它会被保存到这个任务的Future,而且可以通过result()或exception()方法得到。

from concurrent importfuturesdeftask(n):print('{}: starting'.format(n))raise ValueError('the value {} is no good'.format(n))

ex= futures.ThreadPoolExecutor(max_workers=2)print('main: starting')

f= ex.submit(task, 5)

error=f.exception()print('main: error: {}'.format(error))try:

result=f.result()exceptValueError as e:print('main: saw error "{}" when accessing result'.format(e))

如果在一个任务函数中抛出一个未处理的异常后调用了result(),那么会在当前上下文中再次抛出同样的异常。

1.7 上下文管理器

执行器会与上下文管理器合作,并发的运行任务并等待它们都完成。当上下文管理器退出时,会调用执行器的shutdown()方法。

from concurrent importfuturesdeftask(n):print(n)

with futures.ThreadPoolExecutor(max_workers=2) as ex:print('main: starting')

ex.submit(task,1)

ex.submit(task,2)

ex.submit(task,3)

ex.submit(task,4)print('main: done')

离开当前作用域时如果要清理线程或进程资源,那么用这种方式使用执行器就很有用。

内容来源于网络,如有侵权请联系客服删除

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值