首页 文章

如何使用计划库运行异步功能?

提问于
浏览
0

我正在使用discord.py重写编写一个discord bot,我想在某个时间每天运行一个函数 . 我根本没有异步功能的经验,我无法弄清楚如何在不使用“await”的情况下运行它 . 这只是我的代码的一部分,这就是为什么有些东西可能没有被定义的原因 .

async def send_channel():
    try:
        await active_channel.send('daily text here')
    except Exception:
        active_channel_id = None
        active_channel = None

async def timer():
    while True:
        schedule.run_pending()
        await asyncio.sleep(3)
        schedule.every().day.at("21:57").do(await send_channel())

@bot.event
async def on_ready():
    print("Logged in as")
    print(bot.user.name)
    print(bot.user.id)
    print("------")

    bot.loop.create_task(timer())

使用 schedule.every().day.at("00:00").do() 函数,当我将 await send_channel() 放在 .do() 的参数中时出现此错误:

self.job_func = functools.partial(job_func,* args,** kwargs)TypeError:第一个参数必须是可调用的

但是当我不使用await时,我只有 send_channel() 作为参数,我得到这个错误:

RuntimeWarning:从未等待过coroutine'send_channel'

我不是非常擅长编程,所以如果有人可以尝试为我愚蠢,那将是非常棒的 .

谢谢

1 回答

  • 0

    你're doing doesn'工作是因为 do 接受一个函数(或另一个可调用的函数),但你试图 await 或调用一个函数,然后将结果传递给它 .

    await send_channel() 阻塞,直到发送完成,然后给你 None ,这不是一个函数 . send_channel() 返回一个你可以稍后等待做一些工作的协程,这也不是一个函数 .

    如果你传递它只是 send_channel ,那么,这是一个函数,但它是一个 ascynio 协程函数, schedule 将不知道如何运行 .


    此外,不是试图将 schedule 集成到 asyncio 事件循环中,而是想出如何将异步作业包装为 schedule 任务,反之亦然,等等,将 schedule 给自己的线程要容易得多 .

    There's a FAQ entry on this

    如何在不阻塞主线程的情况下连续运行调度程序?在单独的线程中运行调度程序 . Mrwhick在这里为这个问题编写了一个很好的解决方案(查找run_continuously()) .

    基本思路很简单 . 将 timer 函数更改为:

    schedstop = threading.Event()
    def timer():
        while not schedstop.is_set():
            schedule.run_pending()
            time.sleep(3)
    schedthread = threading.Thread(target=timer)
    schedthread.start()
    

    在程序开始之前,甚至在开始 asyncio 事件循环之前执行此操作 .

    在退出时,停止调度程序线程:

    schedstop.set()
    

    现在,要添加任务,它不会在您的顶级代码中,或在异步协程中,或在 scheduler 任务中,您只需添加如下:

    schedule.every().day.at("21:57").do(task)
    

    现在,回到你的第一个问题 . 您要运行的任务是't a normal function, it'是一个 asyncio 协程,它必须作为主事件循环的一部分在主线程上运行 .

    但这正是call_soon_threadsafe的用途 . 你想叫的是:

    bot.loop.call_soon_threadsafe(send_channel)
    

    要让 scheduler 运行它,你只需将 bot.loop.call_soon_threadsafe 作为函数传递,并将 send_channel 作为参数传递 .

    所以,把它们放在一起:

    schedule.every().day.at("21:57").do(
        bot.loop.call_soon_threadsafe, send_channel)
    

相关问题