如何使用Ctrl + C正常关闭协程? - python

我在写蜘蛛来爬行网页。我知道异步可能是我最好的选择。因此,我使用协程来异步处理工作。现在我开始思考如何通过键盘中断退出程序。在完成所有工作之后,该程序可能会关闭。源代码可以在python 3.5中运行,并在下面附加。

import asyncio
import aiohttp
from contextlib import suppress

class Spider(object):
    def __init__(self):
        self.max_tasks = 2
        self.task_queue = asyncio.Queue(self.max_tasks)
        self.loop = asyncio.get_event_loop()
        self.counter = 1

    def close(self):
        for w in self.workers:
            w.cancel()

    async def fetch(self, url):
        try:
            async with aiohttp.ClientSession(loop = self.loop) as self.session:
                with aiohttp.Timeout(30, loop = self.session.loop):
                    async with self.session.get(url) as resp:
                        print('get response from url: %s' % url)
        except:
            pass
        finally:
            pass

    async def work(self):
        while True:
            url = await self.task_queue.get()
            await self.fetch(url)
            self.task_queue.task_done()

    def assign_work(self):
        print('[*]assigning work...')
        url = 'https://www.python.org/'
        if self.counter > 10:
            return 'done'
        for _ in range(self.max_tasks):
            self.counter += 1
            self.task_queue.put_nowait(url)

    async def crawl(self):
        self.workers = [self.loop.create_task(self.work()) for _ in range(self.max_tasks)]
        while True:
            if self.assign_work() == 'done':
                break
            await self.task_queue.join()
        self.close()

def main():
    loop = asyncio.get_event_loop()
    spider = Spider()
    try:
        loop.run_until_complete(spider.crawl())
    except KeyboardInterrupt:
        print ('Interrupt from keyboard')
        spider.close()
        pending  = asyncio.Task.all_tasks()
        for w in pending:
            w.cancel()
            with suppress(asyncio.CancelledError):
                loop.run_until_complete(w)
    finally:
        loop.stop()
        loop.run_forever()
        loop.close()

if __name__ == '__main__':
    main()

但是,如果在运行时按“ Ctrl + C”,则可能会发生一些奇怪的错误。我的意思是有时可以通过Ctrl + C正常关闭程序。没有错误讯息。但是,在某些情况下,按“ Ctrl + C”后程序仍将运行,并且直到所有工作完成后才停止。如果那时我按Ctrl + C,则表示“任务已销毁,但仍在等待中!”会在那里。

我已经阅读了一些有关asyncio的主题,并在main()中添加了一些代码以优雅地关闭协程。但这行不通。有人有类似的问题吗?

python参考方案

我打赌这里会发生问题:

except:
    pass

你should never do这样的事情。您的情况是其他情况的又一个示例。

当您取消任务并等待其取消时,asyncio.CancelledError在任务内部升起,而shouldn't be在任务内部的任何位置都被取消。等待任务取消的行会引发此异常,否则任务将继续执行。

这就是为什么

task.cancel()
with suppress(asyncio.CancelledError):
    loop.run_until_complete(task)  # this line should raise CancelledError, 
                                   # otherwise task will continue

实际取消任务。

更新:

但是我仍然很难理解为什么原始代码可以很好地退出
'Ctrl + C'的概率不确定?

它取决于您的任务状态:

如果目前按“ Ctrl + C”,则所有任务都已完成,
它们将在等待时引发CancelledError,并且您的代码将正常完成。
如果此时按“ Ctrl + C”,则某些任务正在等待执行,但快要完成它们的执行时,您的代码将在任务取消上停留一段时间,并在紧随其后的任务完成时结束。
如果此刻您按“ Ctrl + C”,则某些任务正在等待处理,
距离完成还很远,您的代码会试图取消这些任务(
无法完成)。另一个“ Ctrl + C”将中断
取消,但那时任务不会被取消或完成,您会得到
警告“任务已被破坏,但仍在等待中!”。

如何锁定终端运行的perl,obj c,c++,python和ruby等脚本的源代码? - python

我想出售我在perl,obj c,c ++,python,ruby,bash,php等中制作的脚本等它们都在终端中运行。 (Linux)如何锁定源代码,以便无需人们访问源代码即可分发我的脚本..?换句话说,如何将在Terminal中运行的程序的源代码锁定,以便人们可以使用该程序(如果该代码已下载到他们的Linux机器上,但他们无法访问实际的源代码)?例:ex…

Python sqlite3数据库已锁定 - python

我在Windows上使用Python 3和sqlite3。我正在开发一个使用数据库存储联系人的小型应用程序。我注意到,如果应用程序被强制关闭(通过错误或通过任务管理器结束),则会收到sqlite3错误(sqlite3.OperationalError:数据库已锁定)。我想这是因为在应用程序关闭之前,我没有正确关闭数据库连接。我已经试过了: connectio…

Javascript + Python:将数组发送到Python脚本,将结果返回给Javascript - javascript

我想建立一个网页,该网页通过Javascript API进行许多Facebook状态更新,并将它们分类到一个数组中。然后,我想将此数组发送到Python脚本,该脚本可以专门使用NLTK.进行语言分析。在Python中获得合适的结果后,我想将结果从该脚本返回到Javascript,以显示给用户等。听起来可能吗? javascript大神给出的解决方案 是的,完…

如何在PyQt4的动态复选框列表中检查stateChanged - python

所以我要从PyQt4的列表中添加复选框。但是我找不到在Window类中对每个状态使用stateChanged的方法。这是从列表元素添加它们的功能: def addCheckbox(self): colunas = Graphic(self.caminho).getColunas() for col in colunas: c = QtGui.QCheckBo…

Python pytz时区函数返回的时区为9分钟 - python

由于某些原因,我无法从以下代码中找出原因:>>> from pytz import timezone >>> timezone('America/Chicago') 我得到:<DstTzInfo 'America/Chicago' LMT-1 day, 18:09:00 STD…