如何使用日程表库运行异步功能?

问题描述 投票:3回答:3

我正在使用discord.py rewrite编写一个discord机器人,我想每天在特定时间运行一个函数。我完全没有异步函数的经验,并且我不知道如何在不使用“等待”的情况下运行一个异步函数。这只是我的代码的一部分,这就是为什么可能无法定义某些内容的原因。

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:从未等待协程'send_channel'

我不太擅长编程,所以如果有人可以尝试为我愚弄它,那就太好了。

谢谢

python python-3.x async-await schedule discord.py-rewrite
3个回答
2
投票

您正在做的事不起作用,因为do接受了一个[[function(或另一个可调用),但是您正在尝试await或调用一个函数,然后将结果传递给它。] >await send_channel()会阻塞,直到发送完成,然后再给您None,这不是功能。 send_channel()返回一个协程,您可以稍后等待它做一些工作,它也不是函数。

如果仅通过send_channel传递了它,那是一个函数,但它是ascynio协程函数,schedule不知道如何运行。


而且,与其尝试将schedule集成到asyncio事件循环中,并弄清楚如何将异步作业包装为schedule任务,反之亦然,等等,不如将schedule ]自己的线程。

There's a FAQ entry on this

如何连续运行调度程序而不阻塞主线程?

在单独的线程中运行调度程序。 Mrwhick为此问题写了一个很好的解决方案here(寻找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)


现在,回到您的第一个问题。您要运行的任务不是正常功能,它是asyncio协程,它必须在主线程上作为主事件循环的一部分运行。

但是这正是call_soon_threadsafe的目的。您要呼叫的是:

call_soon_threadsafe

要让bot.loop.call_soon_threadsafe(send_channel)
运行它,您只需传递scheduler作为函数,并传递bot.loop.call_soon_threadsafe作为参数。

所以,把它们放在一起:

send_channel


2
投票
这是一个古老的问题,但是我最近遇到了同样的问题。您可以使用schedule.every().day.at("21:57").do( bot.loop.call_soon_threadsafe, send_channel) 将协程安排到事件循环(而不是回调):

0
投票
[另一种选择是使用run_coroutine_threadsafe的AsyncIOScheduler,它与异步功能(例如asyncio.run_coroutine_threadsafe(async_function(), bot.loop) )更自然地工作。就您而言,您可以简单地编写以下形式的内容:
© www.soinside.com 2019 - 2024. All rights reserved.