推荐学习书目
› 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
Ritter
V2EX  ›  Python

大佬们 我这协程写的有毛病吗?

  •  
  •   Ritter · Jan 19, 2020 · 8125 views
    This topic created in 2481 days ago, the information mentioned may be changed or developed.

    想用 Python 做一个后台扫描工具
    仿照 asyncio 官网的生产者消费者模式写了一个 demo
    发现运行到一半会阻塞住(有时会,有时不会?)
    但我看着貌似不会卡住啊
    求大佬救救孩子

    55 replies  •  2020-01-20 12:15:40 +08:00
    Ritter
    Ritter
    OP
    Jan 19, 2020
    1
    对了
    生产者是从本地文件夹读取字典文件
    消费者拼接 url 发起请求
    Ritter
    Ritter
    OP
    Jan 19, 2020
    2
    难受
    cz5424
    cz5424
    Jan 19, 2020 via iPhone
    3
    你贴代码吧这样描述没人能解答
    Ritter
    Ritter
    OP
    Jan 19, 2020
    4
    @cz5424 大佬上面有代码
    cz5424
    cz5424
    Jan 19, 2020 via iPhone
    5
    @Ritter 阻塞的时候中断看看阻塞在哪个语句
    Ritter
    Ritter
    OP
    Jan 19, 2020
    6
    simple2025
    simple2025
    Jan 19, 2020❤️1
    7
    你的日志也太少了把!!
    put 的日志打印一些?
    ipwx
    ipwx
    Jan 19, 2020❤️1
    8
    大哥 readFromFolder 是阻塞的,你在 async def put 里面得用线程池去执行它。asyncio 的主线程是单线程,没法执行这种阻塞函数。

    https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor
    ipwx
    ipwx
    Jan 19, 2020
    9
    顺便吐槽一句楼上,这么明显的问题不是一眼就能看出来么
    Ritter
    Ritter
    OP
    Jan 19, 2020
    10
    @cz5424 Ctrl C 中断不了
    ipwx
    ipwx
    Jan 19, 2020
    11
    除了 readFromFolder, f.read() 也是阻塞的,也得放在 executor 里面
    Ritter
    Ritter
    OP
    Jan 19, 2020
    12
    @ipwx 可是他是执行到一半的时候阻塞了 self.q.put()这句代码是可以执行到的
    ipwx
    ipwx
    Jan 19, 2020
    13
    @Ritter 楼主也没放日志(差评)。

    不过 async def put 这个函数问题太大了,怎么都会出问题的。
    ipwx
    ipwx
    Jan 19, 2020
    14
    === 我发现楼主还有个问题,在 async def run 里面。

    他只创建了 consumer = asyncio.gather(...),但是没有勒令 consumer 进入执行啊?按照道理 asyncio.gather 并不具有执行一个 coroutine 的特性啊,只有 await 才能保证让一个 coroutine 进入运行状态啊?

    楼主你得用 loop.create_task 把一个 coroutine 强行进入后台运行状态才对吧?
    ipwx
    ipwx
    Jan 19, 2020
    15
    顺便 loop.create_task 就不用 await 了
    Ritter
    Ritter
    OP
    Jan 19, 2020
    16
    @ipwx 代码都是官网抄的 怎么会出错我也不知道啊(狗头)
    Ritter
    Ritter
    OP
    Jan 19, 2020
    17
    @ipwx asyncio.gather 之后确实是会运行的 我试过了
    simple2025
    simple2025
    Jan 19, 2020
    18
    @Ritter 日志,日志呢?
    ipwx
    ipwx
    Jan 19, 2020
    19
    @Ritter 好吧我看了一眼文档,它当真会自动把 coroutine 变成 Task 给 schedule 起来。

    "If any awaitable in aws is a coroutine, it is automatically scheduled as a Task."
    ynkkdev
    ynkkdev
    Jan 19, 2020
    20
    赞同 ipwx,楼主你也要明白,目前 python 协程面临最大的问题的绝大多数第三方库均是同步的,不能支持协程异步。虽然现在已经与很多库在努力的兼容协程,但是在协程处理 io 库时,一定要请楚是否支持。不支持协程的 io 都要通过线程池来处理。官网也给出了 asyncio 中线程池的用法,可以再看看
    Ritter
    Ritter
    OP
    Jan 19, 2020
    21
    @chenqh 大佬 我打印了一下 貌似是在 self.q.get()这卡住了
    Ritter
    Ritter
    OP
    Jan 19, 2020
    22
    @youngce 请求库是 aiohttp 异步的 意思是 读取文件那里卡住了吗
    Ritter
    Ritter
    OP
    Jan 19, 2020
    23
    @ipwx 大佬 asyncio 的队列能跨线程使用吗
    Ritter
    Ritter
    OP
    Jan 19, 2020
    24
    @chenqh put 这个函数是可以运行完的
    simple2025
    simple2025
    Jan 19, 2020
    25
    @Ritter 先把日志打出来呀
    simple2025
    simple2025
    Jan 19, 2020
    26
    @Ritter 我怀疑你 put 跑完了,但是 consumer 还是卡在哪里
    ipwx
    ipwx
    Jan 19, 2020
    27
    @Ritter run_in_executor 本来就是把一个阻塞函数扔到别的线程里面执行,然后把结果拿出来的。

    def fn():
    ....something to do

    await loop.run_in_executor(fn)
    freshgoose
    freshgoose
    Jan 19, 2020
    28
    看来 py 的协程还是很多坑啊……这么说现阶段还是用 golang 写并发比较好?
    Ritter
    Ritter
    OP
    Jan 19, 2020
    29
    @chenqh 好像是 但是我后面一句已经把 consumer 取消了
    Vegetable
    Vegetable
    Jan 19, 2020
    30
    @ipwx #8 并不是无法执行阻塞,只是会阻塞 eventloop 而已。这地方不会卡死。这个方法内部的操作也并不是耗时操作。
    Vegetable
    Vegetable
    Jan 19, 2020❤️1
    31
    你这个程序由于 crawl 是不会主动跳出的,所以当任务执行完毕之后,所有 await queue.get 都会阻塞,等待新的任务入队,是卡在这里吗?
    Ritter
    Ritter
    OP
    Jan 19, 2020
    32
    @Vegetable
    async def run(self):
    crawls = [self.crawl(i) for i in range(self.max_concurrency)]
    consumer = asyncio.gather(*crawls)
    ...
    # cancel consumer
    consumer.cancel()

    我这里已经把 consumer 取消了
    BBrother
    BBrother
    Jan 19, 2020
    33
    有个库叫做 aiofile,你的文件读取是阻塞的
    Ritter
    Ritter
    OP
    Jan 19, 2020
    34
    @BBrother 可是 put 函数是可以完整运行完的 这里是运行到一半的时候卡住了
    simple2025
    simple2025
    Jan 19, 2020
    35
    @Ritter 我一般不是这么退出的, 我一般是再 producer 那边放入特殊的字符串,比如"__end__",然后 consumer 那边接受处理,自己退出的, 你试一试?
    simple2025
    simple2025
    Jan 19, 2020
    36
    @Ritter 你这个就是 consumer 的退出问题,导致的
    jyyx
    jyyx
    Jan 19, 2020❤️2
    37
    消费者那里抛异常, self.q.task_done 并没有执行
    加 try finally 试下
    Vegetable
    Vegetable
    Jan 19, 2020
    38
    #37 jyyx 说的对,报异常会导致任务消费出问题,join()那里会卡住。
    BBrother
    BBrother
    Jan 19, 2020
    39
    @Ritter 你的卡住是指程序运行到一半不动了,并且没有输出,也没有执行完,而且还是概率触发?
    Ritter
    Ritter
    OP
    Jan 19, 2020
    40
    @chenqh https://asyncio.readthedocs.io/en/latest/producer_consumer.html 官网第一个例子应该是你说的这种处理方式 我之前试过貌似也会卡住
    Ritter
    Ritter
    OP
    Jan 19, 2020
    41
    @jyyx
    @Vegetable 可是异常不会向上传播吗?
    Ritter
    Ritter
    OP
    Jan 19, 2020
    42
    @BBrother 输出是有输出的 就是有概率会阻塞
    ipwx
    ipwx
    Jan 19, 2020
    43
    @Ritter 异常无论是不是向上传播,q.task_done 都不能执行了呀,然后 join() 一定会卡住啊。。。

    try:
    ...
    finally:
    q.task_done()
    Vegetable
    Vegetable
    Jan 19, 2020
    44
    @Ritter 因为你没有直接 await crawl,所以这个异常应该是不会传播的。程序不会因此退出
    pmispig
    pmispig
    Jan 19, 2020
    45
    请问这是什么字体,看着真舒服
    Ritter
    Ritter
    OP
    Jan 19, 2020
    46
    @jyyx
    @Vegetable
    @ipwx
    是报异常了 原因也是因为异常导致 put 的数量和 task_done 不一致
    学到了 感谢各位大佬的鼎力相助
    谢谢谢谢~~
    simple2025
    simple2025
    Jan 19, 2020
    47
    log 呀
    Ritter
    Ritter
    OP
    Jan 19, 2020❤️1
    48
    @pmispig 这是 carbon 网站生成的图片 样式用的是 VScode 的
    Ritter
    Ritter
    OP
    Jan 19, 2020
    49
    @chenqh 已经解决了 大佬 感谢回复
    hehe12dyo
    hehe12dyo
    Jan 19, 2020
    51
    朋友 建议你一边读一边把数据往队列里面丢。这样在读大文件读时候看起来好些。
    不然一个 10m 的字典,想想就刺激。
    其实这工具我写过。。
    cz5424
    cz5424
    Jan 19, 2020 via iPhone
    52
    @ipwx 楼上根本就没看代码,手动狗头
    Ritter
    Ritter
    OP
    Jan 19, 2020
    53
    @hehe12dyo 一边读一边写我也想过 但是自带的 open 是阻塞的 上面有位大佬说的 aiofile 有空会去研究一下
    p0wd3rop
    p0wd3rop
    Jan 19, 2020
    54
    这种扫描小工具建议用 Go 写,快,容易理解,很香。
    Yooloo
    Yooloo
    Jan 20, 2020
    55
    go
    go
    go
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   5263 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 89ms · UTC 06:28 · PVG 14:28 · LAX 22:28 · JFK 01:28
    ♥ Do have faith in what you're doing.