Discord 客户端和 Asyncio 多个任务在一段时间后变得疯狂
Discord Client & Asyncio multiple Tasks going all crazy after a while
好的,我有一个 Discord 机器人,在 main.py
:
中这样声明
import discord
import asyncio
#
import settingsDB
import logger
#
import module_one
import module_two
import module_three
import module_four
[...]
client = discord.Client()
botToken = settingsDB.getBotSetting("DiscordToken")
# on_ready event stuff
@client.event
async def on_ready():
# stuff
# [...]
# on_message event stuff
@client.event
async def on_message(message):
# stuff
# [...]
# Tasks functions
async def taskOne():
while True:
cycle = settingsDB.getBotSetting("taskOne_Cycle")
# calling for stuff in module_one.py
# stuff
# [...]
await asyncio.sleep(int(cycle))
async def taskTwo():
while True:
cycle = settingsDB.getBotSetting("taskTwo_Cycle")
# calling for stuff in module_two.py
# stuff
# [...]
await asyncio.sleep(int(cycle))
async def taskThree():
while True:
cycle = settingsDB.getBotSetting("taskThree_Cycle")
# calling for stuff in module_three.py
# stuff
# [...]
await asyncio.sleep(int(cycle))
async def taskFour():
while True:
cycle = settingsDB.getBotSetting("taskFour_Cycle")
# calling for stuff in module_four.py
# stuff
# [...]
await asyncio.sleep(int(cycle))
client.run(botToken)
settingsDB
指的是 settingsDB.py
查询我的数据库的所有函数都存储在其中,并且可以根据通过与 Discord bot 无关的另一个来源更新的用户输入进行更改(通过网站 PHP ).
logger
指的是 logger.py
我正在将我想要作为日志的 txt 文件内容写入其中。
在 on_ready
事件下我写道:
@client.event
async def on_ready():
print('Logged in as {0.user}'.format(client))
try:
asyncio.ensure_future(taskOne())
asyncio.ensure_future(taskTwo())
asyncio.ensure_future(taskThree())
asyncio.ensure_future(taskFour())
except BaseException as err:
logger.logger('Unexpected error > "' + str(err) + '" / "' + str(type(err)) + '"')
raise
我在前几天多次更改了这部分,因为我想要 真正的 异步行为,但无法实现。
但是无论我在这里写了什么,我注意到几个小时后,4 个任务,甚至机器人本身 都是随机启动的,而不是根据到每个 task###()
末尾的 await asyncio.sleep(int(cycle))
。
最奇怪的部分是机器人本身正在触发 print
行,因此告诉我它再次登录 (???)
我不得不提一下,taskOne()
可以根据它处理的内容而有很大差异,可以从 1 分钟到近 20 分钟不等。
知道为什么会这样吗?
如果您需要更多详细信息,请告诉我。
根据@PythonPro 的建议,我更改了on_ready
函数:
G_hasLaunched = False
@client.event
async def on_ready():
global G_hasLaunched
print('Logged in as {0.user}'.format(client))
if G_hasLaunched == False:
G_hasLaunched = True
try:
print("creating tasks")
asyncio.ensure_future(taskOne())
asyncio.ensure_future(taskTwo())
asyncio.ensure_future(taskThree())
asyncio.ensure_future(taskFour())
except BaseException as err:
logger.logger('Unexpected error > "' + str(err) + '" / "' + str(type(err)) + '"')
raise
仍在尝试弄清楚它是否解决了整个问题
如果任务处于阻塞状态(非异步)且耗时过长,则机器人与 Discord 的连接会超时,从用户的角度来看,机器人已离线。当控制返回到机器人的连接逻辑时,它会重新连接到 Discord。重新连接后,它将再次触发 on_ready
事件。
如果需要执行阻塞逻辑,可以参考。要处理每次重新连接时触发 on_ready
,您应该在脚本开头设置一个变量 False
。然后,在on_ready
中检查变量是否为False
。如果是,将其设置为 True
并启动模块中的任务。这里的关键点是任务只启动一次。
discord
模块有 tasks
可以用于这样的目的。不建议您为此使用 while
循环。
而是使用您正在使用的 discord 模块中可用的 tasks.loop()
。
请注意,这必须从 discord.ext
手动导入
所以它看起来像这样:
from discord.ext import tasks
@bot.event
async def on_ready():
if not taskOne.is_running():
taskOne.start()
# Do this for other tasks as well
@tasks.loop(seconds=0)
async def taskOne():
# Do your things here and write other tasks that you want run in background
cycle = settingsDB.getBotSetting("taskOne_Cycle")
# calling for stuff in module_one.py
# stuff
# [...]
await asyncio.sleep(int(cycle))
现在我们已经创建了一个非阻塞循环,这个循环将在机器人上线时启动。
注意 :
我们首先如果任务已经是 运行,如果不是,那么我们才启动它,这是为了防止任务创建自身的多个实例,因为 on_ready
可能会被调用多次。
好的,我有一个 Discord 机器人,在 main.py
:
import discord
import asyncio
#
import settingsDB
import logger
#
import module_one
import module_two
import module_three
import module_four
[...]
client = discord.Client()
botToken = settingsDB.getBotSetting("DiscordToken")
# on_ready event stuff
@client.event
async def on_ready():
# stuff
# [...]
# on_message event stuff
@client.event
async def on_message(message):
# stuff
# [...]
# Tasks functions
async def taskOne():
while True:
cycle = settingsDB.getBotSetting("taskOne_Cycle")
# calling for stuff in module_one.py
# stuff
# [...]
await asyncio.sleep(int(cycle))
async def taskTwo():
while True:
cycle = settingsDB.getBotSetting("taskTwo_Cycle")
# calling for stuff in module_two.py
# stuff
# [...]
await asyncio.sleep(int(cycle))
async def taskThree():
while True:
cycle = settingsDB.getBotSetting("taskThree_Cycle")
# calling for stuff in module_three.py
# stuff
# [...]
await asyncio.sleep(int(cycle))
async def taskFour():
while True:
cycle = settingsDB.getBotSetting("taskFour_Cycle")
# calling for stuff in module_four.py
# stuff
# [...]
await asyncio.sleep(int(cycle))
client.run(botToken)
settingsDB
指的是 settingsDB.py
查询我的数据库的所有函数都存储在其中,并且可以根据通过与 Discord bot 无关的另一个来源更新的用户输入进行更改(通过网站 PHP ).
logger
指的是 logger.py
我正在将我想要作为日志的 txt 文件内容写入其中。
在 on_ready
事件下我写道:
@client.event
async def on_ready():
print('Logged in as {0.user}'.format(client))
try:
asyncio.ensure_future(taskOne())
asyncio.ensure_future(taskTwo())
asyncio.ensure_future(taskThree())
asyncio.ensure_future(taskFour())
except BaseException as err:
logger.logger('Unexpected error > "' + str(err) + '" / "' + str(type(err)) + '"')
raise
我在前几天多次更改了这部分,因为我想要 真正的 异步行为,但无法实现。
但是无论我在这里写了什么,我注意到几个小时后,4 个任务,甚至机器人本身 都是随机启动的,而不是根据到每个 task###()
末尾的 await asyncio.sleep(int(cycle))
。
最奇怪的部分是机器人本身正在触发 print
行,因此告诉我它再次登录 (???)
我不得不提一下,taskOne()
可以根据它处理的内容而有很大差异,可以从 1 分钟到近 20 分钟不等。
知道为什么会这样吗?
如果您需要更多详细信息,请告诉我。
根据@PythonPro 的建议,我更改了on_ready
函数:
G_hasLaunched = False
@client.event
async def on_ready():
global G_hasLaunched
print('Logged in as {0.user}'.format(client))
if G_hasLaunched == False:
G_hasLaunched = True
try:
print("creating tasks")
asyncio.ensure_future(taskOne())
asyncio.ensure_future(taskTwo())
asyncio.ensure_future(taskThree())
asyncio.ensure_future(taskFour())
except BaseException as err:
logger.logger('Unexpected error > "' + str(err) + '" / "' + str(type(err)) + '"')
raise
仍在尝试弄清楚它是否解决了整个问题
如果任务处于阻塞状态(非异步)且耗时过长,则机器人与 Discord 的连接会超时,从用户的角度来看,机器人已离线。当控制返回到机器人的连接逻辑时,它会重新连接到 Discord。重新连接后,它将再次触发 on_ready
事件。
如果需要执行阻塞逻辑,可以参考on_ready
,您应该在脚本开头设置一个变量 False
。然后,在on_ready
中检查变量是否为False
。如果是,将其设置为 True
并启动模块中的任务。这里的关键点是任务只启动一次。
discord
模块有 tasks
可以用于这样的目的。不建议您为此使用 while
循环。
而是使用您正在使用的 discord 模块中可用的 tasks.loop()
。
请注意,这必须从 discord.ext
手动导入
所以它看起来像这样:
from discord.ext import tasks
@bot.event
async def on_ready():
if not taskOne.is_running():
taskOne.start()
# Do this for other tasks as well
@tasks.loop(seconds=0)
async def taskOne():
# Do your things here and write other tasks that you want run in background
cycle = settingsDB.getBotSetting("taskOne_Cycle")
# calling for stuff in module_one.py
# stuff
# [...]
await asyncio.sleep(int(cycle))
现在我们已经创建了一个非阻塞循环,这个循环将在机器人上线时启动。
注意 :
我们首先如果任务已经是 运行,如果不是,那么我们才启动它,这是为了防止任务创建自身的多个实例,因为 on_ready
可能会被调用多次。