前言
预计阅读时长:。。 min
原文链接:Python3中两种基本的多线程方式
原作者:Gh Qi,如需引用转载,请注明来源。
写在前面
1. 多线程与多进程
2. 两种简单的多线程编程实现方式
2.1 使用threading库进行多线程编程
2.2 使用concurrent库进行多线程编程
executor.map()函数
executor.map
是 concurrent.futures
模块中 ThreadPoolExecutor
和 ProcessPoolExecutor
的一个便捷方法,它用于将一个函数应用于可迭代对象的每个元素上,并收集结果。下面是 executor.map 的参数意义:
func: 要应用于每个输入项的函数。
*iterables: 一个或多个可迭代对象,executor.map 将 func 应用于这些可迭代对象的元素。
用人话讲:
executor.map(func,*iterables)
的第二个参数中的iterables
是一个列表,里面存放的是func
函数的参数,func
函数需要几个参数,列表iterables
中就有几个元素,每个元素都是一个包含了所有线程所需的该参数值的元组。
例如:
定义一个求两数之和函数:sum(a,b)
. 我想求1和2的和,3和4的和,5和6的和,这件事安排给3个线程来干。参数可以写为:
data_to_process = [(1, 2), (3, 4), (5, 6)]
要想使用executor.map(func,*iterables)
,需作如下转换:
*zip(*data_to_process) # 第二个参数
print(*zip(*data_to_process))
(1, 3, 5) (2, 4, 6)
iterables
里面给定参数组数可以低于max_workers,那么并不是所有的线程都会被分配工作。线程池中的线程会等待直到有任务可做,或者直到所有任务完成。当然也可以高于max_workers,那就只能排队干活了。
executor.map
返回的是一个可迭代对象,它按顺序提供了 func 应用于每个输入项的结果。在 Python 3.7 之前,它返回一个列表;从 Python 3.7 开始,它返回一个 concurrent.futures._base._as_completed 对象,这个对象是可迭代的,但不是列表。
<generator object Executor.map.<locals>.result_iterator at 0x000001FB535D72E0>
要打印每个线程执行函数的输出结果,可以迭代 executor.map 返回的对象;也可以使用list()
将其转化为一个列表。
# 方法1
with ThreadPoolExecutor(max_workers=5) as executor:
results = executor.map(process_data, *zip(*data_to_process))
print(results)
for result in results:
print(result)
# 方式2
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(process_data, *zip(*data_to_process)))
print(results)
输出是:
<generator object Executor.map.<locals>.result_iterator at 0x000002A7435F3790>
3
7
11
[3, 7, 11]
完整代码:
from concurrent.futures import ThreadPoolExecutor
# 定义要执行的函数
def process_data(arg1, arg2):
# 这里可以是任何需要两个参数的逻辑
return arg1 + arg2
# 创建数据列表,每个元素是一个包含两个参数的元组
# 我想求1和2的和,3和4的和,5和6的和,这件事安排给3个线程来干
data_to_process = [(1, 2), (3, 4), (5, 6)]
# 创建线程池(最大线程数不超过5各)
with ThreadPoolExecutor(max_workers=5) as executor:
# 使用 zip 将 data_to_process 中的元组解包为两个参数
results = list(executor.map(process_data, *zip(*data_to_process)))
# 打印每个线程的输出结果
for result in results:
print(result)