V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
fghjghf
V2EX  ›  Python

求解, python3.5 的 asyncio 库, async/await 的一些疑惑

  •  
  •   fghjghf · 2020-04-15 18:14:28 +08:00 · 4071 次点击
    这是一个创建于 1464 天前的主题,其中的信息可能已经有所发展或是发生改变。

    async/await 这个协程 异步为什么对磁盘 io 无效?

    import asyncio
    async def wr1(): # f = open('text1.txt','w') # for x in range(1,39999999): # f.write('12345ddd') # f.close()

    f = open('text3.txt','r')
    print(f.read())
    f.close()
    

    async def wr2(): # f = open('text2.txt','w') # for x in range(1,79999999): # f.write('23456fff') # f.close()

    for x in range(1,519999):
    	print('xx')
    

    async def wr3(): # f = open('text3.txt','w') # for x in range(1,99999999): # f.write('34567ggg') # f.close()

    for x in range(1,919999):
    	print('yy')
    

    def demo4():

    async def washing1():
        await wr1()
    
    async def washing2():
        await wr2()
    
    async def washing3():
        await wr3()
    
    
    # 1. 创建一个事件循环
    loop = asyncio.get_event_loop()
    
    # 2. 将异步函数加入事件队列
    tasks = [
        washing1(),
        washing2(),
        washing3(),
    ]
    
    # 3. 执行事件队列, 直到最晚的一个事件被处理完毕后结束
    loop.run_until_complete(asyncio.wait(tasks))
    
    
    # 4. 如果不再使用 loop, 建议养成良好关闭的习惯
    loop.close()
    

    if name == 'main': start = time()

     demo4()  
    
    end = time()
    print('elapsed time = ' + str(end - start))
    

    第一段是读取磁盘,第二第三段是打印。按道理遇到 io,dma 交出总线,cpu 就空运转直到有数据才工作,所以 wr1 应该和 2 或 3 一块执行才对的,而实际情况不是这样的,是跟单线程同步执行一样的所需时间

    lucays
        1
    lucays  
       2020-04-15 18:21:41 +08:00
    washing1(),washing2(),washing3()不要 await wr1(),wr2(), wr3()这么搞,这样本来就和同步一样了
    lniwn
        2
    lniwn  
       2020-04-15 18:22:48 +08:00   ❤️ 1
    首先你得明白协程的概念,不是加个 async 修饰就能叫异步函数的。如果使用了 asyncio,那 io 以及 sleep 等操作,就要使用 asyncio 包内的封装函数。或者使用 executor 。
    你的代码里 open 操作卡死整个线程,导致没法 yield 到其他协程,所以只能顺序执行。
    CosimoZi
        3
    CosimoZi  
       2020-04-15 18:24:32 +08:00   ❤️ 3
    不是你说他是协程它就是协程的,它得是协程它才是一个协程.
    https://github.com/Tinche/aiofiles
    gwy15
        4
    gwy15  
       2020-04-15 18:27:00 +08:00
    在异步函数里面调用同步函数还是会阻塞的啊,你得用异步的文件接口。官方库里面没有,得用线程池,或者你自己造。
    iyaozhen
        5
    iyaozhen  
       2020-04-15 18:44:56 +08:00
    这就是 Python 这些语言半路出家搞协程的缺点

    底层很多东西没有异步,不是简单加个 async 关键字就解决问题了

    其实目前绝大部分异步方案都无法做到文件 IO 的异步,都是线程池去模拟的
    iyaozhen
        6
    iyaozhen  
       2020-04-15 18:46:26 +08:00
    janxin
        7
    janxin  
       2020-04-15 18:50:26 +08:00 via iPhone
    需要选择支持异步功能的 io 库
    fghjghf
        8
    fghjghf  
    OP
       2020-04-15 22:01:01 +08:00
    @lniwn 不懂求解。那就是说,必须是 asyncio 包内封装的函数,例如 asyncio.sleep(3)这种才有用吗?(我也测过,asyncio.sleep 函数的确有效,会快很多,但这不是真的 io 操作啊)。那遇到真的 io 操作如何是好?其实我是想和 tornado 搭配用的。tornado6 不支持 @gen.coroutine 了,必须用 async/await 。我的理解 ioloop 是通过 epoll 的 in out error 非堵塞做到单线程高并发的,遇到 EAGAIN 就用协程继续执行另一段逻辑。不知道这样理解是否有错
    Trim21
        9
    Trim21  
       2020-04-15 22:09:26 +08:00
    网络 io 有原生协程,文件 io 要要协程只能用线程包起来,没法原生异步。
    fghjghf
        10
    fghjghf  
    OP
       2020-04-15 22:36:36 +08:00
    @Trim21 不懂求解。
    from tornado.web import RequestHandler
    from tornado import gen
    import tornado.ioloop

    class IndexHandler(RequestHandler):
    def get(self):
    self.write('this is index')

    class BlockingHandler(RequestHandler):
    async def get(self):
    result = await self.dosomething()
    self.write(result)

    async def dosomething(self):
    # 如果是其他处理函数或者逻辑,要保证函数是协程
    await gen.sleep(20)
    return 'block end'

    app = tornado.web.Application([
    (r"/index", IndexHandler),
    (r"/blocking", BlockingHandler)
    ])
    if __name__ == "__main__":
    app.listen(80)
    tornado.ioloop.IOLoop.instance().start()

    那 async/await 用在 tornado6 上有啥作用呢?一定要 asyncio 内包装的函数才有效。但 IOLoop 已经用 epoll 实现网络 io 复用了。实际应用中,堵塞的不都是磁盘 io 么。那这么做的目的又是啥呢
    Trim21
        11
    Trim21  
       2020-04-15 22:47:31 +08:00
    @fghjghf #10 io 复用和异步 io 是两个概念吧,async/await 是为了解决 io 阻塞,不负责解决 io 是否复用的问题。
    fghjghf
        12
    fghjghf  
    OP
       2020-04-15 22:58:54 +08:00
    @Trim21 但网络 io 的堵塞问题,封装 eopll 的 ioloop 已经帮我们处理了。底层有个队列。socket 非堵塞执行,epollin 和 out 的时候才能寻找对应路由表执行逻辑。EAGAIN 就 modify epoll 的状态,继续执行队列的下一个,如此类推。那就是能走到路由表的 client socket 的网络 io 绝对是可用的。根本不存在堵塞啊。那 tornado 上的 async/await 具体有啥作用呢,不知道我这理解有没有错
    Trim21
        13
    Trim21  
       2020-04-15 23:19:07 +08:00
    @fghjghf #12 这个是 python 语言内的异步。之前 python 同步调用(比如 requests.get )是没法在 io 阻塞的时候让出解释器的。
    PTLin
        14
    PTLin  
       2020-04-15 23:33:36 +08:00
    lolizeppelin
        15
    lolizeppelin  
       2020-04-16 02:19:45 +08:00
    没学会走就开始跑就这结果了

    正正经经的把异步编程写几次,什么 select,epoll 多线程都好好用过几次再来折腾协程吧

    别堕落到低端 php 程序员的水准好么
    neoblackcap
        16
    neoblackcap  
       2020-04-16 03:00:36 +08:00
    async/await 本质上是对 IO 复用机制的封装,系统提供的 IO 复用不能完成的事情,async/await 也不能实现。比如你想用底层是 epoll 实现的 eventloop 去处理文件 IO 那是不行的,要不就是模拟。
    去读《 Unix 网络编程》吧,回来就会更好地理解这些语法
    dreamapple
        17
    dreamapple  
       2020-04-16 03:41:02 +08:00 via Android
    python 的异步不完备吧,现在没必要在上面浪费时间
    conn4575
        18
    conn4575  
       2020-04-16 07:28:29 +08:00 via Android
    官方 group 有讨论过这个问题,由于底层系统不支持,现在的异步本地 io 都是多线程包装的,实际的提升很小,所以 asycio 才没有做这个
    CzaOrz
        19
    CzaOrz  
       2020-04-16 08:43:57 +08:00
    --- async/await 这个协程 异步为什么对磁盘 io 无效?

    可以看看官网文档,明确说了对磁盘 IO 不支持,他们推荐 `loop.run_in_executor`
    lniwn
        20
    lniwn  
       2020-04-16 09:40:24 +08:00
    @fghjghf #8 11,16,19 楼已经回答这个问题了。
    Orenoid
        21
    Orenoid  
       2020-04-16 09:48:54 +08:00
    因为 python 的协程本质上是借助 IO 多路复用来实现异步的,磁盘 IO 没有相应的机制和接口
    fghjghf
        22
    fghjghf  
    OP
       2020-04-16 10:03:38 +08:00
    @lolizeppelin 不看我上面的回复吗?张嘴就来???
    pmispig
        23
    pmispig  
       2020-04-16 10:22:08 +08:00
    python 的协程就是搞笑的,别花太多心思,底层大部分不支持。比如网络 io,你用 socket 弄到死都不能异步,你得用
    asyncio.open_connection(host,22),
    ipwx
        24
    ipwx  
       2020-04-16 10:23:39 +08:00
    @pmispig ... asyncio.XXX 也是 socket 啊,不是 socket 库的才是 socket 啊。你这批判毫无道理啊。
    ipwx
        25
    ipwx  
       2020-04-16 10:24:13 +08:00
    @pmispig 就好像你用 java,用基础 socket 它也一样不异步呀。你得用异步框架比如 vert.x
    pmispig
        26
    pmispig  
       2020-04-16 10:27:04 +08:00
    @ipwx 看你跟什么比,你用 go 的,最基础的 net.Conn 就是支持的,python 要是不能让 socket 支持,那异步有个毛用呢,现在这么多库都是基于 socket,得要人家全部重写
    ipwx
        27
    ipwx  
       2020-04-16 10:33:22 +08:00
    @pmispig

    go 新出来了,大家要造新类库,可以这没问题。

    asyncio 新出来了,大家要造新类库,不行这很垃圾。

    不觉得这么评价很奇怪么?
    youxiachai
        28
    youxiachai  
       2020-04-16 10:35:37 +08:00   ❤️ 1
    为啥宁愿发帖都不看看官方文档...
    https://github.com/python/asyncio/wiki/ThirdParty#filesystem
    我一个不搞 python 都能找到答案...
    sikong31
        29
    sikong31  
       2020-04-16 11:10:54 +08:00
    这需要库专门支持,比如 requests 不行得用 aiohttp
    不像多线程,直接往里面放就行了,多线程该阻塞的还是阻塞,只不过不在一个线程
    协程是阻塞的时候直接转向 loop 里的下一个任务,所以这里面就有一个操作,就是告诉系统你待会儿来找我,
    普通的库并没有这个操作,只会让你等着
    就像买东西,有些人你能打个招呼,待会等做完了来取东西,有些只能等着不能走
    no1xsyzy
        30
    no1xsyzy  
       2020-04-16 15:09:31 +08:00
    @pmispig #26 跟 go 比异步支持那是作死,整个语言设计和运行时设计就是想着异步来的。
    你有本事跟 erlang 比分布式系统啊,go 还要 grpc,那之前用 xmlrpc 的那些是不是都得重写?
    何况这叫协程,一个不接受协调的程序放里面不能如你想象地运作,不是很符合语义吗?

    你需要的是 gevents 吧……
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3059 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 14:44 · PVG 22:44 · LAX 07:44 · JFK 10:44
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.