Tôi đang viết một con nhện để thu thập thông tin các trang web. Tôi biết asyncio có lẽ là lựa chọn tốt nhất của tôi. Vì vậy, tôi sử dụng coroutines để xử lý công việc không đồng bộ. Bây giờ tôi gãi đầu của tôi về làm thế nào để thoát khỏi chương trình bằng cách ngắt bàn phím. Chương trình có thể đóng cửa tốt sau khi tất cả các công trình đã được thực hiện. Mã nguồn có thể được chạy trong python 3.5 và được attatched dưới đây.Làm thế nào để duyên dáng tắt coroutines với Ctrl + C?
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()
Nhưng nếu tôi nhấn 'Ctrl + C' trong khi đang chạy, một số lỗi lạ có thể xảy ra. Tôi có nghĩa là đôi khi chương trình có thể được tắt bởi 'Ctrl + C' một cách duyên dáng. Không có thông báo lỗi. Tuy nhiên, trong một số trường hợp, chương trình sẽ vẫn chạy sau khi nhấn 'Ctrl + C' và sẽ không dừng cho đến khi tất cả các công việc đã được thực hiện. Nếu tôi nhấn 'Ctrl + C' tại thời điểm đó, 'Tác vụ đã bị hủy nhưng đang chờ xử lý!' Sẽ ở đó.
Tôi đã đọc một số chủ đề về asyncio và thêm một số mã trong main() để đóng coroutines một cách duyên dáng. Nhưng nó không hoạt động. Có ai khác có vấn đề tương tự không?
Tôi cho rằng bạn đã đúng. 'ngoại trừ: vượt qua' là trường hợp! Tôi thêm 'nâng cao' sau khi 'vượt qua' trong 'ngoại trừ:' và nó có thể thoát tốt bằng 'Ctrl + C'. Vì vậy, nếu tôi muốn đăng nhập các lỗi, tôi nên reraise trường hợp ngoại lệ để chính() có thể bắt những ngoại lệ bao gồm asyncio.CancelledError. Nhưng tôi vẫn khó hiểu tại sao mã gốc có thể thoát ra tốt bằng 'Ctrl + C' với xác suất không chắc chắn? Nếu cấu trúc 'try-except' trong fetch() có thể nắm bắt tất cả các ngoại lệ, main() sẽ capture không có gì, do đó lỗi sẽ xuất hiện mọi lúc. – xssl
@xssl, tôi đã cập nhật câu trả lời để hiển thị những gì có thể xảy ra trong các trường hợp khác nhau. –