Python多线程-线程池ThreadPoolExecutor

1. 线程池

为什么要使用线程池?

1)、多线程中, 线程的数量并非越多越好;

不是线程数量越多,程序的执行效率就越快。线程也是一个对象,是需要占用资源的,线程数量过多的话肯定会消耗过多的资源,同时线程间的上下文切换也是一笔不小的开销,所以有时候开辟过多的线程不但不会提高程序的执行效率,反而会适得其反使程序变慢,得不偿失。

2)、节省每次开启线程的开销。

为了防止无尽的线程被初始化,于是线程池就诞生了。当线程池初始化时,会自动创建指定数量的线程,有任务到达时直接从线程池中取一个空闲线程来用即可,当任务执行结束时线程不会消亡而是直接进入空闲状态,继续等待下一个任务。而随着任务的增加线程池中的可用线程必将逐渐减少,当减少至零时,任务就需要等待了。这就最大程度的避免了线程的无限创建,当所需要使用的线程不知道有多少时,一般都会使用线程池。

在 python 中使用线程池有两种方式,一种是基于第三方库 threadpool,另一种是基于 python3 新引入的库 concurrent.futures.ThreadPoolExecutor,这里我们介绍一下后一种。

concurrent.futures.ThreadPoolExecutor,在提交任务的时候有两种方式,一种是submit()函数,另一种是map()函数,两者的主要区别在于:

1)、map可以保证输出的顺序, submit输出的顺序是乱的。

2)、如果你要提交的任务的函数是一样的,就可以简化成map。但是假如提交的任务函数是不一样的,或者执行的过程之可能出现异常(使用map执行过程中发现问题会直接抛出错误)就要用到submit()。

3)、submit和map的参数是不同的,submit每次都需要提交一个目标函数和对应的参数,map只需要提交一次目标函数,目标函数的参数放在一个迭代器(列表,字典)里就可以。

2.submit方法

  • ThreadPoolExecutor

ThreadPoolExecutor(max_workers=None, thread_name_prefix='', initializer=None, initargs=())
最常用的是 max_workers 参数,即线程池中的线程数。

  • submit

submit(fn, *args, **kwargs)
其中 fn 为方法名,其后的 *args, **kwargs 为该方法的参数。

  • 示例:
import  threading
from concurrent.futures import ThreadPoolExecutor
import time
import random

# 对两数进行加法,并停留0-60随机秒数
def add(a, b):
    sum = a + b
    slp = random.randint(0,60)
    time.sleep(slp)
    print('{} is running, {} + {} = {} ,thread sleep {} seconds'.format(threading.current_thread().name,a,b,sum,slp))

# 加法的两个因数
list_a = [1, 2, 3, 4, 5, 6 ,7]
list_b = [6, 7, 8, 9, 10 ,11 ,12]
# 使用with上下问管理器,就不用管如何关线程池了
with ThreadPoolExecutor(2) as pool:
    # 将每一个线程都进行提交
    for i in range(len(list_a)):
        pool.submit(add, list_a[i], list_b[i])

运行截图:

 

3.map方法

  • map :

map(func, *iterables, timeout=None, chunksize=1)

func 参数为多线程指向的方法名,*iterables 实际上是该方法的参数,该方法的参数必须是可迭代对象,即元组或列表等,不能单纯的传递 int 或字符串,如果 timeout 设定的时间小于线程执行时间会抛异常 TimeoutError,默认为 None 则不加限制。

使用 map 方法,有两个特点:

无需提前使用submit方法
返回结果的顺序和元素的顺序相同,即使子线程先返回也不会获取结果

  • 示例:
import time
import random
import threading
from concurrent.futures import ThreadPoolExecutor

def add(a, b):
    sum = a + b
    slp = random.randint(0,60)
    time.sleep(slp)
    print('{} is running, {} + {} = {} ,thread sleep {} seconds'.format(threading.current_thread().name,a,b,sum,slp))
    return sum

list_a = [1, 2, 3, 4, 5, 6 ,7]
list_b = [6, 7, 8, 9, 10 ,11 ,12]
with ThreadPoolExecutor(2) as pool:
    # map中list_a与list_b按照下标一一对应
    for result in pool.map(add, list_a, list_b):
        print('result = {}'.format(result))

运行截图:

 

posted @ 2024-07-05 16:10  业余砖家  阅读(4)  评论(0编辑  收藏  举报