Python 的异步如何应用在普通操作上?

目前网上能找到的关于 Python 的异步操作,几乎都是各种 HTTP 请求或者 Sockets 连接。然而却鲜有文章讲解如何用异步来进行普通的操作。

例如我有一百个 1G 的 txt 文件,我想让 Python 异步读取他们。这个操作用 Python 3.6 的 async 与 await 如何实现呢?

如果我有很多普通的 Python 操作,例如复制文件,删除大文件等等操作,如何使用异步而不是多线程来完成这些操作?

如果在 Golang 里面,可以把这些操作放在一个函数中,然后go delete('xxx.txt') 他就会异步去执行了。可是在 Python 里面,似乎有点让人摸不着头脑。


Python 的异步如何应用在普通操作上?

18 回复

文件 io 的话可以看下这个 https://github.com/Tinche/aiofiles

用协程主要是为了 io 操作不阻塞下一步的代码被执行,可以充分利用 cpu,所以很多例子都是网络 io 方面的


异步的核心是在等待I/O时让出控制权,而不是让CPU空等。在普通操作里,如果操作本身不涉及I/O(比如纯计算),用异步没啥意义,反而可能因为事件循环开销更慢。但如果你指的“普通操作”是文件读写、网络请求这些,那异步就很有用了。

举个例子,假设你要下载三个网页,用同步写法就是一个接一个等,总时间是三个请求的和。用异步可以同时发起所有请求,总时间差不多是最慢的那个。

import aiohttp
import asyncio

async def fetch_url(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    urls = [
        'http://httpbin.org/delay/1',
        'http://httpbin.org/delay/2',
        'http://httpbin.org/delay/3'
    ]
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        print(f'Got {len(results)} results')

if __name__ == '__main__':
    asyncio.run(main())

这个代码里,三个请求几乎是同时发起的,总共耗时约3秒(最慢的那个),而不是6秒。关键点:1)用async def定义协程;2)在需要等待的地方用await;3)用asyncio.gather并发运行多个协程。

注意:异步不是多线程,它还是单线程,只是在一个任务等待时切换到另一个任务。所以CPU密集型任务用异步不会更快,这时候该用多进程。

总结:异步适合I/O密集型场景,能让你的代码在等待时干别的活。

只是标准库不提供这个功能,而且本质上来说,网络 IO 在普通 Web 应用里更容易阻塞,本地 IO 一般会更快。何况,实在不行还能上 SSD 不是

实际上因为并非所有的操作都有第三方库支持,所以我希望能找到一种通用的异步开发方法。

不改任何代码想实现的话直接 eventlet 就好了

只有阻塞 api 的操作,可以参考:


python<br> req_future = asyncio.get_event_loop().run_in_executor(<br> self.executor,<br> lambda: session.send(req)<br> )<br> r = await asyncio.wait_for(req_future, 15)<br>

这个可以实现对任何操作进行异步吗?

这里的 self.executor 可以是任何函数吗?

既然是 executor 那就应该是个 executor 呀

那这个 executor 是什么东西。写法有要求吗

refer to <a target="_blank" href="https://www.baidu.com/s?wd=python+executor" rel="nofollow noopener">https://www.baidu.com/s?wd=python+executor</a>

你这个想法我有过,经过几个月的学习和尝试,事实告诉我这可能做不到。

eventlet 通过 hack os 库 实现不改代码的 write read 异步
但是你想不去深入,希望库帮你什么都异步好是做不到的
不了了解具体原理你用都用不好

老老实实学习异步的实现过程再找最适合的来用

python<br>from concurrent.futures import ThreadPoolExecutor as Pool<br>filenames = [...]<br>def readFile(filename):<br> with open(filename, encoding='utf-8') as f:<br> content = f.read()<br> return content # or do what you want<br><br>with Pool(10) as executor:<br> results = executor.map(readFile, filenames)<br> for result in results:<br> print(result)<br>

用 ThreadPoolExecutor 就可以咯

你们都理解错题意了。读文件只是举个例子。我希望能实现对任何操作进行异步处理,并且能自定义回调函数。就像 JavaScript 一样。你这个代码没有实现回调函数的功能阿。


仔细看下文档就知道了,如果要回调,就使用 ThreadPoolExecutor().submit(func, args),会返回一个 future 对象,他有 add_done_callback 方法。文档在这,https://docs.python.org/dev/library/concurrent.futures.html#concurrent.futures.Future.add_done_callback
想看例子的,可以看下这篇文章
http://masnun.com/2016/03/29/python-a-quick-introduction-to-the-concurrent-futures-module.html

concurrent.futures 是个特别容易使用的异步库,哈哈。twisted 什么的太复杂了,async/await 也挺不错的,写法也还简单,不过题主问的问题是要把普通操作变成异步的……那就是把普通函数做成异步的,我是这么理解的,所以就用这个最合适了。

io 异步就用 epoll/select 监控 fd

密集计算中间自己控制放弃 cpu 一般用协程,yeid 和 greenlet 之类

封装来封装去底层最后的实现基本都这样,c 怎么写 python 也怎么写

回到顶部