Python 如何使用调度库运行异步函数?

Python 如何使用调度库运行异步函数?,python,python-3.x,async-await,schedule,discord.py-rewrite,discord.py,Python,Python 3.x,Async Await,Schedule,Discord.py Rewrite,Discord.py,我正在使用discord.py rewrite编写一个discord机器人,我希望每天在某个特定的时间运行一个函数。我对异步函数没有任何经验,我不知道如何在不使用“wait”的情况下运行异步函数。这只是我的一段代码,这就是为什么有些东西可能没有定义的原因 async def send_channel(): try: await active_channel.send('daily text here') except Exception: acti

我正在使用discord.py rewrite编写一个discord机器人,我希望每天在某个特定的时间运行一个函数。我对异步函数没有任何经验,我不知道如何在不使用“wait”的情况下运行异步函数。这只是我的一段代码,这就是为什么有些东西可能没有定义的原因

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()
函数,当我将
wait wait send\u channel()
放入
.do()的参数中时,会出现此错误:

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

但是,当我不使用wait,而只使用
send\u channel()
作为参数时,我会得到以下错误:

运行时警告:从未等待协同路由“发送\ U频道”

我不太擅长编程,所以如果有人能帮我把它弄哑,那就太棒了


谢谢

您正在做的事情不起作用,因为
do
接受一个函数(或另一个可调用函数),但您正在尝试
等待
或调用一个函数,然后将结果传递给它

wait send_channel()
阻塞直到发送完成,然后给出
None
,这不是一个函数
send_channel()
返回一个协同程序,您可以稍后等待它来完成一些工作,而这也不是一个函数

如果您只传递了
send\u channel
,那么这是一个函数,但它是一个
ascynio
corroutine函数,
schedule
将不知道如何运行


此外,与其尝试将
调度
集成到
异步IO
事件循环中,并找出如何将异步作业包装为
调度
任务,反之亦然等等,只需给
调度
它自己的线程就更容易了

:

如何在不阻塞主线程的情况下连续运行计划程序?

在单独的线程中运行调度程序。Mrwhick为这个问题写了一个很好的解决方案(查找run_continuously())

基本思想很简单。将
定时器
功能更改为:

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()
现在,要添加任务,不管您是在顶级代码中,还是在异步协同程序中,还是在
调度程序中,您都可以这样添加它:

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

现在,回到你的第一个问题。您想要运行的任务不是一个普通函数,它是一个
asyncio
coroutine,必须作为主事件循环的一部分在主线程上运行

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

bot.loop.call_soon_threadsafe(send_channel)
要要求调度程序运行该命令,只需传递
bot.loop.call_soon\u threadsafe
作为函数,并将
send\u channel
作为参数

因此,把所有这些放在一起:

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

这是一个老问题,但我最近遇到了同样的问题。您可以使用来计划到事件循环的协同路由(而不是回调):

另一种选择是使用的AsyncIOScheduler,它可以更自然地与异步函数(如
发送\ U通道
)一起工作。在您的情况下,您可以简单地编写以下内容:

scheduler = AsyncIOScheduler()
scheduler.add_job(send_channel, trigger=tr)
scheduler.start()
其中
tr
是一个触发器对象。您可以使用间隔为1天且开始日期为21:57的
IntervalTrigger
,也可以使用
CronTrigger


请注意,在程序结束时,建议对调度程序对象调用
shutdown()

中的内置解决方案是使用
discord.ext.tasks
扩展名。这允许您注册一个任务,以便在特定的时间间隔内重复调用。当机器人启动时,我们会将循环的启动延迟到目标时间,然后每24小时运行一次任务:

import asyncio
from discord.ext import commands, tasks
from datetime import datetime, timedelta

bot = commands.Bot("!")

@tasks.loop(hours=24)
async def my_task():
    ...

@my_task.before_loop
async def before_my_task():
    hour = 21
    minute = 57
    await bot.wait_until_ready()
    now = datetime.now()
    future = datetime.datetime(now.year, now.month, now.day, hour, minute)
    if now.hour >= hour and now.minute > minute:
        future += timedelta(days=1)
    await asyncio.sleep((future-now).seconds)

my_task.start()

通过同样的问题,我找到了一个混合了一些先前解决方案的解决方案:

导入计划
从discord.ext导入任务
@tasks.loop(小时=24)
异步def发送_通道():
通过
稍后在我定义主线程之前

def事件启动程序(func):
如果没有,函数是否正在运行()
函数开始()
schedstop=threading.Event()
def timer():
而不是schedstop.is_set():
schedule.run_pending()
睡眠(1)
schedthread=threading.Thread(目标=计时器)
schedthread.start()
最后,在主线中:

如果名称=“\uuuuu main\uuuuuuuu”:
...
计划。每()天。在('21:57:00').do(活动开始,发送频道)
...

是否需要螺纹部分?因为我不能让它工作,而且我对线程不是很熟悉(我知道我应该熟悉)。在“schedule.every().day.at”(“21:57”).do(bot.loop.call_soon\u threadsafe,send\u channel)”方面,这部分对我也不起作用。我仍然收到一个错误,从未等待过协同程序“发送频道”。我认为send_channel()函数必须具有async才能工作。
import asyncio
from discord.ext import commands, tasks
from datetime import datetime, timedelta

bot = commands.Bot("!")

@tasks.loop(hours=24)
async def my_task():
    ...

@my_task.before_loop
async def before_my_task():
    hour = 21
    minute = 57
    await bot.wait_until_ready()
    now = datetime.now()
    future = datetime.datetime(now.year, now.month, now.day, hour, minute)
    if now.hour >= hour and now.minute > minute:
        future += timedelta(days=1)
    await asyncio.sleep((future-now).seconds)

my_task.start()