Python 异步模块concurrent
引言
在计算机编程中,传统的同步模型通常会导致性能瓶颈,特别是在处理大量的输入输出操作时。为了充分利用计算机资源和提高程序的运行效率,异步编程成为一种越来越流行的技术。Python 作为一门主打简洁和易读性的编程语言,也提供了多种异步编程的方式。在本文中,我们将详细介绍 Python 中的异步模块 concurrent。
什么是异步编程
在传统的同步编程模型中,程序的执行是按照一条线路依次执行的,每一行代码执行完成后才会执行下一行代码。这种方式会导致在执行慢速的 I/O 操作时,程序会阻塞在这一行代码上,无法执行其他任务,从而浪费了计算机的资源。
异步编程则采用了一种非阻塞的执行方式,当一个慢速 I/O 操作被触发时,程序会继续执行后面的代码,而不是停下来等待 I/O 操作的结果。当 I/O 操作完成时,程序会通过回调的方式接收到结果,并继续执行后续的操作。
异步编程的优势在于可以更充分地利用计算机资源,提高程序的运行效率。特别是在处理大量的 I/O 操作时,异步编程可以显著地提高程序的性能。
concurrent 模块简介
Python 的 concurrent 模块是 Python 3.2 版本后引入的标准库,用于支持异步编程。concurrent 模块提供了一系列的工具和类,用于开发异步程序。
其中最主要的类是 ThreadPoolExecutor
和 ProcessPoolExecutor
,它们分别提供了基于线程和进程的异步执行环境。通过这些类,我们可以方便地创建线程池和进程池,并且在其中执行任务。
除了线程池和进程池,concurrent 模块还提供了其他一些辅助工具,比如 as_completed
函数和 wait
函数,它们可以用于并发地执行多个任务,并监听任务的完成事件。
接下来,我们将详细介绍 concurrent 模块的使用方法,并通过示例代码演示其运行效果。
异步编程的基本原理
在介绍 concurrent 模块之前,我们先来了解一下异步编程的基本原理。
在异步编程中,程序通常会被组织成一个个的 任务,每一个任务代表一个需要执行的操作。这些任务会被提交到一个 执行环境 中,执行环境会负责调度和执行这些任务。
执行环境可以是一个线程池,也可以是一个进程池。在执行环境中,每个任务都会被分配给一个 执行器 进行执行。执行器会并发地执行多个任务,并通过回调函数将任务的结果返回给程序。
在执行环境中,还会有一个事件循环,用于监听任务的完成事件。当一个任务完成时,事件循环会调用相应的回调函数,将任务的结果传递给程序。
通过这种方式,我们可以将计算密集型或慢速 I/O 的任务交给执行环境处理,从而实现并发执行,提高程序的性能。
使用 ThreadPoolExecutor 创建线程池
在使用 concurrent 模块之前,我们需要先安装它。可以通过以下命令使用 pip 安装:
$ pip install concurrent
安装完成后,我们就可以开始使用 concurrent 的各种功能了。首先,我们来展示如何使用 ThreadPoolExecutor
类创建一个线程池。
import concurrent.futures
def task(name):
print(f'Task {name} is starting...')
result = name + ' is finished'
print(f'Task {name} is finished')
return result
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
futures = []
for i in range(5):
future = executor.submit(task, i)
futures.append(future)
for future in concurrent.futures.as_completed(futures):
print(future.result())
在这个例子中,我们首先定义了一个 task
函数,它接受一个参数 name
,并返回一个带有任务结果的字符串。在这个函数中,我们打印了任务的开始和结束信息,并模拟了一个耗时的任务。
然后,我们使用 ThreadPoolExecutor
类创建了一个最大容量为 5 的线程池,并用 with
语句包裹起来,以确保在使用完线程池后能正确地关闭它。
接下来,我们使用 submit
方法提交了 5 个任务给线程池,每个任务都调用了 task
函数,并将任务的结果返回。
最后,我们使用 as_completed
函数监听任务的完成事件,并使用 result
方法获取任务的结果。注意,as_completed
函数返回的是一个生成器,在 for 循环中逐个迭代任务的结果。
运行以上代码,我们可以看到类似如下的输出:
Task 0 is starting...
Task 1 is starting...
Task 2 is starting...
Task 3 is starting...
Task 4 is starting...
Task 3 is finished
Task 0 is finished
Task 2 is finished
Task 4 is finished
Task 1 is finished
0 is finished
1 is finished
2 is finished
3 is finished
4 is finished
可以看到,线程池并发地执行了 5 个任务,并正确地返回了任务的结果。
使用 ProcessPoolExecutor 创建进程池
与线程池类似,我们也可以使用 ProcessPoolExecutor
类创建一个进程池。下面的示例展示了如何使用 ProcessPoolExecutor
创建进程池。
import concurrent.futures
def task(name):
print(f'Task {name} is starting...')
result = name + ' is finished'
print(f'Task {name} is finished')
return result
with concurrent.futures.ProcessPoolExecutor(max_workers=5) as executor:
futures = []
for i in range(5):
future = executor.submit(task, i)
futures.append(future)
for future in concurrent.futures.as_completed(futures):
print(future.result())
这段代码与之前的线程池示例非常相似,唯一的区别在于我们使用了 ProcessPoolExecutor
类替代了 ThreadPoolExecutor
类。
运行以上代码,我们可以看到类似如下的输出:
Task 0 is starting...
Task 1 is starting...
Task 2 is starting...
Task 3 is starting...
Task 4 is starting...
Task 0 is finished
Task 2 is finished
Task 4 is finished
Task 1 is finished
Task 3 is finished
0 is finished
2 is finished
4 is finished
1 is finished
3 is finished
可以看到,进程池同样并发地执行了 5 个任务,并正确地返回了任务的结果。
并发执行多个任务
除了使用线程池和进程池执行多个任务外,concurrent 模块还提供了其他一些方法,可以用于并发地执行多个任务。
其中之一是 concurrent.futures.as_completed
函数,它可以用于监听一组任务的完成事件,并在任务完成后返回一个生成器,用于访问任务的结果。
下面的示例展示了如何使用 as_completed
函数并发地执行多个任务。
import concurrent.futures
def task(name):
print(f'Task {name} is starting...')
result = name + ' is finished'
print(f'Task {name} is finished')
return result
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
futures = []
for i in range(5):
future = executor.submit(task, i)
futures.append(future)
for future in concurrent.futures.as_completed(futures):
print(future.result())
这段代码与之前的线程池示例非常相似,只是我们使用了 as_completed
函数替代了之前的 for 循环。
运行以上代码,我们可以看到类似如下的输出:
Task 0 is starting...
Task 1 is starting...
Task 2 is starting...
Task 3 is starting...
Task 4 is starting...
Task 4 is finished
Task 1 is finished
Task 3 is finished
Task 2 is finished
Task 0 is finished
4 is finished
1 is finished
3 is finished
2 is finished
0 is finished
可以看到,任务的完成顺序并不是按照任务的提交顺序进行的,而是按照任务完成的时间顺序进行的。
除了 as_completed
函数,concurrent 模块还提供了 concurrent.futures.wait
函数,它可以用于等待一组任务的完成事件。
下面的示例展示了如何使用 wait
函数并发地执行多个任务。
import concurrent.futures
def task(name):
print(f'Task {name} is starting...')
result = name + ' is finished'
print(f'Task {name} is finished')
return result
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
futures = []
for i in range(5):
future = executor.submit(task, i)
futures.append(future)
done, _ = concurrent.futures.wait(futures)
for future in done:
print(future.result())
这段代码与之前的线程池示例非常相似,只是我们使用了 wait
函数替代了之前的 for 循环。
运行以上代码,我们可以看到类似如下的输出:
Task 0 is starting...
Task 1 is starting...
Task 2 is starting...
Task 3 is starting...
Task 4 is starting...
Task 0 is finished
Task 1 is finished
Task 2 is finished
Task 3 is finished
Task 4 is finished
0 is finished
1 is finished
2 is finished
3 is finished
4 is finished
可以看到,wait
函数等待所有任务完成后,再逐个访问任务的结果。
总结
在本文中,我们详细介绍了 Python 中的异步模块 concurrent。我们首先了解了异步编程的基本原理,然后介绍了 concurrent 模块的使用方法,并通过示例代码演示了线程池和进程池的使用,以及并发执行多个任务的方法。
使用 concurrent 模块,我们可以轻松地开发并发程序,并充分利用计算机资源,提高程序的性能。同时,concurrent 模块也给我们提供了更多的灵活性和控制权,使我们能够根据实际需求灵活调整异步执行的方式。