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

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

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


ThreadPoolExecutor


Executor.map 方法

from concurrent import futures
from time import sleep

def printer(n):
        sleep(n)
        print("sleep {}s output {}".format(n, n))
        return n*n

executor = 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))

.result_iterator at 0x7f86398a6620>
sleep 2s output 2
sleep 3s output 3
sleep 0s output 0
sleep 1s output 1
sleep 4s output 4
done! result 16
done! result 9
done! result 4
done! result 1
done! result 0
ThreadPoolExecutor构造方法的max_workers参数是指定线程数,任务开始会执行 printer(4) printer(3) printer(2)
executor.map 方法的结果返回一个生成器,生成器中保存运行结果

Executor.submit和futures.as_completed方法

import concurrent.futures
import urllib.request

URLS = ['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)))

{: 'http://www.baidu.com/', 
: 'http://www.sina.com/', 
: 'http://www.jd.com/', 
: 'http://www.mi.com/', 
: '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.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()
ThreadPoolExecutor.__init__ 方法需要 max_workers 参数, 指定线程池中线程的数量。 在 ProcessPoolExecutor 类中, 那个参数是可选的, 而且大多数情况下不使用——默认值是os.cpu_count() 函数返回的 CPU 数量
ProcessPoolExecutor 和 ThreadPoolExecutor 类都实现了通用的Executor 接口, 因此使用 concurrent.futures 模块能特别轻松地把基于线程的方案转成基于进程的方案。

你可能感兴趣的:(python)