Python 异步: 在 Asyncio 中运行阻塞任务(14)

简介: 阻塞任务是阻止当前线程继续进行的任务。

如果在 asyncio 程序中执行阻塞任务,它会停止整个事件循环,从而阻止任何其他协程继续进行。

我们可以通过 asyncio.to_thread() 和 loop.run_in_executor() 函数在 asyncio 程序中异步运行阻塞调用。

1. 阻塞任务

asyncio的重点是异步编程和非阻塞IO。然而,我们经常需要在 asyncio 应用程序中执行阻塞函数调用。

这可能有很多原因,例如:

  • 执行 CPU 密集型任务,例如计算某事。
  • 执行阻塞 IO 绑定任务,如从文件读取或写入。
  • 调用不支持 asyncio 的第三方库。

直接在 asyncio 程序中进行阻塞调用将导致事件循环在执行阻塞调用时停止。它不允许其他协程在后台运行。

我们如何在 asyncio 程序中异步执行阻塞调用?

2. 如何运行阻塞任务

asyncio 模块提供了两种在 asyncio 程序中执行阻塞调用的方法。

第一种是使用 asyncio.to_thread() 函数。这是在高级 API 中,供应用程序开发人员使用。

asyncio.to_thread() 函数采用要执行的函数名和任何参数。

该函数在单独的线程中执行。它返回一个可以作为独立任务等待或安排的协程。

...
# execute a function in a separate thread
await asyncio.to_thread(task)

在返回的协程有机会在事件循环中运行之前,任务不会开始执行。asyncio.to_thread() 函数在后台创建一个 ThreadPoolExecutor 来执行阻塞调用。因此,asyncio.to_thread() 函数仅适用于 IO 绑定任务。

另一种方法是使用 loop.run_in_executor() 函数。

这是在低级异步 API 中,首先需要访问事件循环,例如通过 asyncio.get_running_loop() 函数。

loop.run_in_executor() 函数接受一个执行器和一个要执行的函数。

如果没有为执行器提供,则使用默认执行器,即 ThreadPoolExecutor。

loop.run_in_executor() 函数返回一个可等待对象,如果需要可以等待它。任务将立即开始执行,因此返回的可等待对象不需要等待或安排阻塞调用开始执行。

...
# get the event loop
loop = asyncio.get_running_loop()
# execute a function in a separate thread
await loop.run_in_executor(None, task)

或者,可以创建一个执行器并将其传递给 loop.run_in_executor() 函数,该函数将在执行器中执行异步调用。

在这种情况下,调用者必须管理执行器,一旦调用者完成它就将其关闭。

...
# create a process pool
with ProcessPoolExecutor as exe:
    # get the event loop
    loop = asyncio.get_running_loop()
    # execute a function in a separate thread
    await loop.run_in_executor(exe, task)
    # process pool is shutdown automatically...

这两种方法允许阻塞调用作为异步任务在 asyncio 程序中执行。

现在我们知道如何在 asyncio 程序中执行阻塞调用,让我们看一些有效的例子。

3. 实例

我们可以探索如何使用 asyncio.to_thread() 在 asyncio 程序中执行阻塞 IO 绑定调用。

在这个例子中,我们将定义一个函数来阻塞调用者几秒钟。然后,我们将使用 asyncio.to_thread() 函数在 asyncio 的线程池中异步执行此函数。

这将使呼叫者腾出时间继续其他活动。

# SuperFastPython.com
# example of running a blocking io-bound task in asyncio
import asyncio
import time
 
# a blocking io-bound task
def blocking_task():
    # report a message
    print('Task starting')
    # block for a while
    time.sleep(2)
    # report a message
    print('Task done')
 
# main coroutine
async def main():
    # report a message
    print('Main running the blocking task')
    # create a coroutine for  the blocking task
    coro = asyncio.to_thread(blocking_task)
    # schedule the task
    task = asyncio.create_task(coro)
    # report a message
    print('Main doing other things')
    # allow the scheduled task to start
    await asyncio.sleep(0)
    # await the task
    await task
 
# run the asyncio program
asyncio.run(main())

运行示例首先创建 main() 协程并将其作为 asyncio 程序的入口点运行。main() 协程运行并报告一条消息。然后它发出对线程池的阻塞函数调用的调用。然后将协程包装在任务中并独立执行。

main() 协程可以自由地继续其他活动。在这种情况下,它会休眠片刻以允许计划任务开始执行。这使得目标函数可以在后台下发给 ThreadPoolExecutor 并开始运行。

然后 main() 协程挂起并等待任务完成。阻塞函数报告一条消息,休眠 2 秒,然后报告最后一条消息。

这突出了我们如何在一个单独的线程中与 asyncio 程序异步执行阻塞 IO 绑定任务。

Main running the blocking task
Main doing other things
Task starting
Task done
相关文章
|
4天前
|
存储 NoSQL Redis
在Python Web开发过程中,为什么Redis运行速度快
【5月更文挑战第15天】Redis在Python Web开发中运行速度快,原因包括:1) 丰富数据类型满足多样化需求;2) 简单数据模型提升查询效率;3) 单线程模型结合非阻塞I/O实现高效处理;4) 持久化机制保证数据安全;5) 二进制协议与管道技术优化网络通信。这些因素共同确保Redis能处理大量请求并保持高性能。
24 1
|
1天前
|
网络协议 数据处理 调度
深入探索Python异步编程:asyncio库的应用与实践
在现代软件开发中,异步编程已成为处理并发和I/O密集型任务的重要策略。本文将带您深入探索Python的asyncio库,解析其背后的设计原理,并通过实例展示如何在实际项目中应用asyncio实现高效的异步编程。我们不仅会探讨asyncio的基本用法,还会分析其性能优势,并探讨其与其他并发模型的比较。此外,文章还将涵盖asyncio在Web开发、网络编程和数据处理等场景中的应用案例,帮助您更好地理解并掌握这一强大的异步编程工具。
|
2天前
|
Java 测试技术 Python
Python的多线程允许在同一进程中并发执行任务
【5月更文挑战第17天】Python的多线程允许在同一进程中并发执行任务。示例1展示了创建5个线程打印"Hello World",每个线程调用同一函数并使用`join()`等待所有线程完成。示例2使用`ThreadPoolExecutor`下载网页,创建线程池处理多个URL,打印出每个网页的大小。Python多线程还可用于线程间通信和同步,如使用Queue和Lock。
16 1
|
2天前
|
监控 调度 开发者
Python 中的异步编程:理解 asyncio 库的基本原理与应用
本文将深入探讨 Python 中的异步编程技术,重点介绍 asyncio 库的基本原理与应用。通过解释事件循环、协程和 Future 对象的概念,读者将能够理解异步编程的工作原理,并学会如何利用 asyncio 库提高程序的性能和效率。本文还将通过实际示例演示如何使用 asyncio 库来处理 I/O 密集型任务和 CPU 密集型任务,以及如何避免常见的陷阱和错误。
|
2天前
|
调度 UED Python
Python 中的异步编程:理解 asyncio 库的基本原理与应用
本文探讨了 Python 中的异步编程,重点介绍了 asyncio 库的基本原理与应用。通过分析事件循环、协程以及异步/await 关键字的作用机制,读者将能够深入理解异步编程的工作方式,并学会如何利用 asyncio 库构建高效的异步应用程序。同时,本文还介绍了一些实际案例,帮助读者更好地掌握 asyncio 库的实际应用。
|
4天前
|
SQL 数据采集 数据挖掘
构建高效的Python数据处理流水线:使用Pandas和NumPy优化数据分析任务
在数据科学和分析领域,Python一直是最受欢迎的编程语言之一。本文将介绍如何通过使用Pandas和NumPy库构建高效的数据处理流水线,从而加速数据分析任务的执行。我们将讨论如何优化数据加载、清洗、转换和分析的过程,以及如何利用这些库中的强大功能来提高代码的性能和可维护性。
|
4天前
|
数据采集 Web App开发 数据处理
Lua vs. Python:哪个更适合构建稳定可靠的长期运行爬虫?
Lua vs. Python:哪个更适合构建稳定可靠的长期运行爬虫?
|
4天前
|
缓存 Shell 开发工具
[oeasy]python0016_在vim中直接运行python程序
在 Vim 编辑器中,可以通过`:!`命令来执行外部程序,例如`:!python3 oeasy.py`来运行Python程序。如果想在不退出Vim的情况下运行当前编辑的Python文件,可以使用`%`符号代表当前文件名,所以`:!python3 %`同样能运行程序。此外,可以使用`|`符号连续执行命令,例如`:w|!python3 %`会先保存文件(`w`)然后运行Python程序。这样,就可以在不离开Vim的情况下完成编辑、保存和运行Python程序的流程。
19 0
|
4天前
|
API UED Python
使用Python进行异步HTTP请求的实践指南
使用Python进行异步HTTP请求的实践指南
20 4
|
4天前
|
SQL DataWorks 安全
DataWorks产品使用合集之DataWorks资源里python运行时候,查看中途打印日志如何解决
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
26 0