python concurrent.futures并发库 多进程 多线程

来源:互联网 发布:义乌美工招聘 编辑:程序博客网 时间:2024/06/06 03:27

Python3废弃了原来的thread模块,换成了高级的threading模块,concurrent.futures是使用线程的最新方式。(Python3把thread模块重命名为_thread,以此强调这是低层实现, 不应该在应用代码中使用)如果使用场景较复杂,需要更高级的工具multiprocessing模块和threading模块。

concurrent.futures模块提供了一个用于异步执行调用的高级接口


ThreadPoolExecutor


Executor.map 方法

from concurrent import futuresfrom time import sleepdef printer(n):        sleep(n)        print("sleep {}s output {}".format(n, n))        return n*nexecutor = futures.ThreadPoolExecutor(max_workers=3)results = executor.map(printer, [4,3,2,1,0])for i, result in enumerate(results):        print('done ! result {}: {}'.format(i, result))<generator object Executor.map.<locals>.result_iterator at 0x7f86398a6620>sleep 2s output 2sleep 3s output 3sleep 0s output 0sleep 1s output 1sleep 4s output 4done! result 16done! result 9done! result 4done! result 1done! result 0
ThreadPoolExecutor构造方法的max_workers参数是指定线程数,任务开始会执行 printer(4) printer(3) printer(2)
executor.map 方法的结果返回一个生成器,生成器中保存运行结果

Executor.submit和futures.as_completed方法

import concurrent.futuresimport urllib.requestURLS = ['http://www.baidu.com/',        'http://www.sina.com/',        'http://www.mi.com/',        'http://jd.com/',        'http://taobao.com/']def load_url(url, timeout):    with urllib.request.urlopen(url, timeout=timeout) as conn:        return conn.read()with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}print(future_to_url)    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 at 0x7fef5f684400 state=running>: 'http://www.baidu.com/', <Future at 0x7fef5ece8080 state=running>: 'http://www.sina.com/', <Future at 0x7fef5eccf8d0 state=running>: 'http://www.jd.com/', <Future at 0x7fef5d871860 state=pending>: 'http://www.mi.com/', <Future at 0x7fef5d871908 state=pending>: 'http://taobao.com/'}'http://www.sina.com/' page is 601152 bytes'http://www.baidu.com/' page is 111404 bytes'http://www.mi.com/' page is 294910 bytes'http://taobao.com/' page is 123740 bytes'http://www.jd.com/' page is 123354 bytes
前三个Future的状态是 running, 因为有三个工作的线程。
后两个Future的状态是 pending, 等待有线程可用
executor.submit方法排定可调用对象的执行时间,然后返回Future,表示这个待执行的操作。
存储各个Future,后面传给as_completed函数。
as_completed函数在future运行结束后产出结果。
executor.submit和futures.as_completed这个组合比executor.map 更灵活,因为 submit 方法能处理不同的可调用对象和参数,而 executor.map只能处理参数不同的同一个可调用对象。此外,传给futures.as_completed函数的集合可以来自多个Executor实例,例如一些由 ThreadPoolExecutor 实例创建,另一些由ProcessPoolExecutor实例创建。

ProcessPoolExecutor

这个模块实现的是真正的并行计算,因为它使用ProcessPoolExecutor类把工作分配给多个Python进程处理。因此,如果需要做CPU 密集型处理,使用这个模块能绕开GIL,利用所有可用的CPU核心。
import concurrent.futuresimport mathPRIMES = [    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 Truedef 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()
ThreadPoolExecutor.__init__ 方法需要 max_workers 参数, 指定线程池中线程的数量。 在 ProcessPoolExecutor 类中, 那个参数是可选的, 而且大多数情况下不使用——默认值是os.cpu_count() 函数返回的 CPU 数量
ProcessPoolExecutor 和 ThreadPoolExecutor 类都实现了通用的Executor 接口, 因此使用 concurrent.futures 模块能特别轻松地把基于线程的方案转成基于进程的方案。