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

|  |      1Ritter OP 对了   生产者是从本地文件夹读取字典文件 消费者拼接 url 发起请求 | 
|  |      2Ritter OP 难受 | 
|  |      3cz5424      2020-01-19 15:08:33 +08:00 via iPhone 你贴代码吧这样描述没人能解答 | 
|  |      6Ritter OP | 
|      7chenqh      2020-01-19 15:15:47 +08:00  1 你的日志也太少了把!! put 的日志打印一些? | 
|  |      8ipwx      2020-01-19 15:16:48 +08:00  1 大哥 readFromFolder 是阻塞的,你在 async def put 里面得用线程池去执行它。asyncio 的主线程是单线程,没法执行这种阻塞函数。 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor | 
|  |      9ipwx      2020-01-19 15:17:09 +08:00 顺便吐槽一句楼上,这么明显的问题不是一眼就能看出来么 | 
|  |      11ipwx      2020-01-19 15:17:50 +08:00 除了 readFromFolder, f.read() 也是阻塞的,也得放在 executor 里面 | 
|  |      14ipwx      2020-01-19 15:25:46 +08:00 === 我发现楼主还有个问题,在 async def run 里面。 他只创建了 consumer = asyncio.gather(...),但是没有勒令 consumer 进入执行啊?按照道理 asyncio.gather 并不具有执行一个 coroutine 的特性啊,只有 await 才能保证让一个 coroutine 进入运行状态啊? 楼主你得用 loop.create_task 把一个 coroutine 强行进入后台运行状态才对吧? | 
|  |      15ipwx      2020-01-19 15:26:06 +08:00 顺便 loop.create_task 就不用 await 了 | 
|  |      19ipwx      2020-01-19 15:31:30 +08:00 @Ritter 好吧我看了一眼文档,它当真会自动把 coroutine 变成 Task 给 schedule 起来。 "If any awaitable in aws is a coroutine, it is automatically scheduled as a Task." | 
|      20youngce      2020-01-19 15:33:09 +08:00 赞同 ipwx,楼主你也要明白,目前 python 协程面临最大的问题的绝大多数第三方库均是同步的,不能支持协程异步。虽然现在已经与很多库在努力的兼容协程,但是在协程处理 io 库时,一定要请楚是否支持。不支持协程的 io 都要通过线程池来处理。官网也给出了 asyncio 中线程池的用法,可以再看看 | 
|  |      27ipwx      2020-01-19 15:41:01 +08:00 @Ritter run_in_executor 本来就是把一个阻塞函数扔到别的线程里面执行,然后把结果拿出来的。 def fn(): ....something to do await loop.run_in_executor(fn) | 
|      28freshgoose      2020-01-19 15:44:17 +08:00 看来 py 的协程还是很多坑啊……这么说现阶段还是用 golang 写并发比较好? | 
|  |      30Vegetable      2020-01-19 15:45:12 +08:00 @ipwx #8 并不是无法执行阻塞,只是会阻塞 eventloop 而已。这地方不会卡死。这个方法内部的操作也并不是耗时操作。 | 
|  |      31Vegetable      2020-01-19 15:47:13 +08:00  1 你这个程序由于 crawl 是不会主动跳出的,所以当任务执行完毕之后,所有 await queue.get 都会阻塞,等待新的任务入队,是卡在这里吗? | 
|  |      32Ritter OP @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 取消了 | 
|  |      33BBrother      2020-01-19 15:53:09 +08:00 有个库叫做 aiofile,你的文件读取是阻塞的 | 
|      35chenqh      2020-01-19 15:55:34 +08:00 @Ritter 我一般不是这么退出的, 我一般是再 producer 那边放入特殊的字符串,比如"__end__",然后 consumer 那边接受处理,自己退出的, 你试一试? | 
|      37jyyx      2020-01-19 15:58:53 +08:00  2 消费者那里抛异常, self.q.task_done 并没有执行 加 try finally 试下 | 
|  |      38Vegetable      2020-01-19 16:03:01 +08:00 #37 jyyx 说的对,报异常会导致任务消费出问题,join()那里会卡住。 | 
|  |      40Ritter OP @chenqh https://asyncio.readthedocs.io/en/latest/producer_consumer.html 官网第一个例子应该是你说的这种处理方式 我之前试过貌似也会卡住 | 
|  |      43ipwx      2020-01-19 16:18:19 +08:00 | 
|  |      45pmispig      2020-01-19 16:38:37 +08:00 请问这是什么字体,看着真舒服 | 
|  |      46Ritter OP | 
|      47chenqh      2020-01-19 16:39:18 +08:00 log 呀 | 
|  |      50Ritter OP | 
|      51hehe12dyo      2020-01-19 17:21:01 +08:00 朋友 建议你一边读一边把数据往队列里面丢。这样在读大文件读时候看起来好些。 不然一个 10m 的字典,想想就刺激。 其实这工具我写过。。 | 
|  |      53Ritter OP @hehe12dyo 一边读一边写我也想过 但是自带的 open 是阻塞的 上面有位大佬说的 aiofile 有空会去研究一下 | 
|      54p0wd3rop      2020-01-19 17:54:52 +08:00 这种扫描小工具建议用 Go 写,快,容易理解,很香。 | 
|  |      55KaynW      2020-01-20 12:15:40 +08:00 go go go |