Python标准库-concurrent.futures

concurrent.futures模块提供了一个高层面的接口来实现异步调用。

concurrent,futures提供了两种方式来执行异步调用,一种是借助线程,使用ThreadPoolExecutor对象;另一种是借助进程,使用ProcessPoolExecutor对象,这两种对象实现了相同的接口,这些接口定义在他们的父类Executor中。

Executor对象

这是一个抽象类,提供了异步执行的方法。这个类不应该被直接使用,应该使用上面提到的两个具体子类。

方法介绍

  • submit(fn, *args, **kwargs)

    执行fn(*args, **kwargs)并返回一个Future对象,Future对象在下面会介绍到。

with ThreadPoolExecutor(max_workers=1) as executor:
    future = executor.submit(pow, 323, 1235)
    print(future.result())
  • map(func, *iterables, timeout=None, chunksize=1)

    与内置的map函数用法相似,除了下面两个区别:

    1. iterables不是懒加载的
    2. func是异步执行的,多个func可以并发执行

    timeout指定一个时间,如果next()被调用了且在timeout时间内没有得到结果则引发concurrent.futures.TimeoutError。如果为None,则不限制时间。

    当使用ProcessPoolExecutor时,设置chunksize的值可以将iterables分块,并一次性发给进程池中的对象,对于很长的迭代对象,使用一个大的chunksize可以提高效率。但是对于ThreadPoolExecutor对象,chunksize没有任何作用。
    我的理解是因为进程之间占用了不同的内存空间,所以不同的进程在执行时需要先从调用Executor的进程复制所需的参数,当chunksize为1时,每执行一次就需要通信一次,显然非常浪费时间,所以设置一个大点的chunksize可以一次性获取多次执行所需的参数,减少通信的次数。而线程因为本来就在同一个内存空间中,不存在这个问题,因此没有影响。

  • shutdown(wait=True)

    释放资源的,通过给每个thread或process执行join()方法实现。

    通过使用with语句可以避免使用这个方法。

ThreadPoolExecutor(max_workers=None, thread_name_prefix='')

ThreadPoolExecutor是Executor的子类,通过使用线程池来实现异步调用。

当一个Future关联的可调用对象等待另一个Future的结果时,会发生死锁,官方例子如下:

def wait_on_b():
    time.sleep(5)
    print(b.result())  # b will never complete because it is waiting on a.
    return 5
def wait_on_a():
    time.sleep(5)
    print(a.result())  # a will never complete because it is waiting on b.
    return 6
executor = ThreadPoolExecutor(max_workers=2)
a = executor.submit(wait_on_b)
b = executor.submit(wait_on_a)

还有一个:

def wait_on_future():
    f = executor.submit(pow, 5, 2)
    # This will never complete because there is only one worker thread and
    # it is executing this function.
    print(f.result())
executor = ThreadPoolExecutor(max_workers=1)
executor.submit(wait_on_future)

ProcessPoolExecutor(max_workers=None, thread_name_prefix='')

ProcessPoolExecutor同样也是Executor的子类,通过进程池来实现异步调用,且不受全局解释器锁的限制。

__main__模块必须被工作子进程导入,这意味这ProcessPoolExecutor不会在交互式解释器中起作用。

同样,与线程中提到的一样,不当的使用会引起死锁,具体例子同上。

ProcessPoolExecutor例子

import concurrent.futures
import math

PRIMES = [
    112272535095293,
    112582705942171,
    112272535095293,
    115280095190773,
    115797848077099,
    1099726899285419]

def is_prime(n):
    if n % 2 == 0:
        return False

    sqrt_n = int(math.floor(math.sqrt(n)))
    for i in range(3, sqrt_n + 1, 2):
        if n % i == 0:
            return False
    return True

def main():
    with concurrent.futures.ProcessPoolExecutor() as executor:
        for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
            print('%d is prime: %s' % (number, prime))

if __name__ == '__main__':
    main()

可以看出map函数返回的直接就是结果,与submit有些区别。

Future 对象

Future类封装了可调用对象的异步执行,Future实例由Executor.submit()创建,不应该被直接创建。

方法介绍

  • cancel()

    尝试取消调用,如果调用正在执行且无法被取消则返回False,否则调用会被取消并返回True。

  • cancelled()

    调用成功被取消时返回True。

  • running()

    如果调用正在被执行且不能被取消则返回True。

  • done()

    如果调用成功被取消或者执行完成则返回True。

  • result(timeout=None)

    返回调用的返回值。

  • add_done_callback(fn)

    这个方法用来添加回调函数,调用这个方法的future对象会把自己作为唯一参数传给函数fn,无论future是执行完成还是被取消,都会调用fn。

模块方法

concurrent.futures.wait(fs, timeout=None, return_when=ALL_COMPLETED)

返回两个二元组集合,第一个集合名为done,包含已经完成的futures;第二个集合名为not_done,包含没有完成的futures。return_when参数指明了这个方法什么时候返回,一共有三种参数,默认为ALL_COMPLETED.

1. FIRST_COMPLETED:当任何future完成或被取消时返回
2. FIRST_EXCEPTION:当任何future引起一个异常时返回,如果没有异常,等效于ALL_COMPLETED
3. ALL_COMPLETED:当所有futures完成或被取消时返回

concurrent.futures.as_completed(fs, timeout=None)

返回由fs给出的Future实例迭代器,只有当future完成时才会返回,返回的顺序与完成的顺序相同,最先完成的最先返回,如果fs中包含两个同样的对象,只会返回一次。

官网例子如下:

import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
    with urllib.request.urlopen(url, timeout=timeout) as conn:
        return conn.read()

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))

future_to_url是一个字典,一个可迭代对象。以submit()返回的future作为键,url作为值。执行以后可以发现输出的结果的顺序与传入的url顺序不一定是相等的。

参考连接

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容