如何在 asyncio 中完成任务后退出

时间:2021-02-20 19:47:23

标签: python task python-asyncio wait telethon

在这个简短而简化的代码中,我想等待所有任务完成(queue.join)然后离开。 此代码打开网络连接并下载一些数据。 不幸的是,我必须按 'ctrl-c',因为 'client.run_until_disconnected()'。

完整的代码有效,但我不知道如何在不中断键盘的情况下退出。 我必须用 crontab 来安排它,所以我不能使用 'client.run_until_disconnected()'

编辑:更新代码

#!/usr/bin/env python3.7

import asyncio
import telethon
from telethon import TelegramClient
from telethon import functions, types
from datetime import datetime

api_id = 00000 # your api_id
api_hash = "your api hash"

async def worker(queue):
    while True:
        queue_book = await queue.get()
        book_name = queue_book.file.name
        print(book_name)
        loop = asyncio.get_event_loop()
        await client.download_media(queue_book,book_name)
        queue.task_done()

async def main():
    #Free ebook medical articles
    channel = await client(functions.messages.CheckChatInviteRequest('your channel hash'))
    #values message's id depend on the chosen channel
    ids = [63529,63528,63527,63526,63525,63524,63523,63522]
    queue = asyncio.Queue(1)
    workers = [asyncio.create_task(worker(queue)) for _ in range(5)]
    for booksId in ids:
        async for get_book in client.iter_messages(channel.chat, ids=booksId):
            await queue.put(get_book) 
    await queue.join()
    for the_worker in workers:
        the_worker.cancel()

async def wait_until(dt):
      now = datetime.now()
      await asyncio.sleep((dt - now).total_seconds())

async def run_at(dt, coro):
      await wait_until(dt)
      return await coro

client = TelegramClient("Test", api_id, api_hash)
loop = asyncio.get_event_loop()
client.start()

try:
    loop = loop.create_task(run_at(datetime(2021, 2, 19, 11,00),main()))    

    client.run_until_disconnected()
except KeyboardInterrupt as keyint:
    print("KeyboardInterrupt..")
    pass

1 个答案:

答案 0 :(得分:3)

run_until_disconnected() 只是一个辅助函数,如果您想控制事件循环何时结束,您没有义务运行它。最小的变化是将 client.run_until_disconnected() 替换为 loop.run_forever(),并在 loop.stop() 的末尾调用 main()

更惯用的方法是让 main() 保持原样,但不是以 create_task 开头,而是从您的顶级协程(通常称为main 按照惯例,但您已经为其他函数选择了该名称)。然后你可以直接调用asyncio.run(actual_main()),当main结束时程序会自动退出。例如(未经测试):

# main and other functions as in the question

async def actual_main():
    global client
    client = TelegramClient("Test", api_id, api_hash)
    await client.start()
    await run_at(datetime(2021, 2, 19, 11, 00), main())

asyncio.run(actual_main())