无法从Python中的另一个线程函数触发异步函数

2024-06-17 10:27:48 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在制作一个discord机器人,它会不时使用请求获取json,然后将相关信息发送到特定的通道

我有以下课程:

  • Helper,它是discord bot本身,在asyncio.gather内从一开始就异步运行
  • tasker控制调用将执行请求的类的间隔。它在不同的线程中运行,因此在等待时不会停止异步Helper
  • getInfo执行请求的,存储信息并应与Helper对话

我现在有两个问题:

tasker在不同的线程上时,每次我试图通过getInfoHelper交谈时,它都会给我错误RuntimeError: no running event loopRuntimeWarning: coroutine 'getInfo.discordmsg' was never awaited

但是,如果我不在另一个线程上运行它,它确实可以在TestStatus:1上运行,但是它会使Helper陷入困境,并停止使用TestStatus:2运行

不管怎样,这是代码

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

class Helper(discord.Client):
    async def on_ready(self):
        global discordbot, taskervar
        servername = 'ServerName'
        discordbot = self
        self.servidores = dict()
        self.canais = dict()
        for i in range(len(self.guilds)):
            self.servidores[self.guilds[i].name] = {}
            self.servidores[self.guilds[i].name]['guild']=self.guilds[i]
            servidor = self.guilds[i]
            for k in range(len(servidor.channels)):
                canal = servidor.channels[k]
                self.canais[str(canal.name)] = canal
            if 'bottalk' not in self.canais.keys():
                newchan = await self.servidores[self.guilds[i].name]['guild'].create_text_channel('bottalk')
                self.canais[str(newchan.name)] = newchan
            self.servidores[self.guilds[i].name]['canais'] = self.canais
        self.bottalk = self.get_channel(self.servidores[servername]['canais']['bottalk'].id)
        await self.msg("Bot online: " + converteHora(datetime.now(),True))
        print(f'{self.user} has connected to Discord!')
        taskervar.startprocess()
    async def msg(self, msg):
        await self.bottalk.send(msg)
    async def on_message(self, message):
        if message.author == self.user:
            return
        else:
            print(message)

class tasker:
    def __init__(self):
        global discordbot, taskervar
        print('Tasker start')
        taskervar = self
        self.waiter = threading.Event()
        self.lastupdate = datetime.now()
        self.nextupdate = datetime.now()
        self.thread = threading.Thread(target=self.requests)
    def startprocess(self):
        if not self.thread.is_alive():
            self.waiter = threading.Event()
            self.interval = 60*5
            self.thread = threading.Thread(target=self.requests)
            self.thread.start()
    def requests(self):
        while not self.waiter.is_set():
            getInfo()
            self.lastupdate = datetime.now()
            self.nextupdate = datetime.now()+timedelta(seconds=self.interval)
            self.waiter.wait(self.interval)
    def stopprocess(self):
        self.waiter.set()

class getInfo:
    def __init__(self):
        global discordbot, taskervar
        self.requests()
    async def discordmsg(self,msg):
        await discordbot.msg(msg)
    def requests(self):
        jsondata = {"TestStatus": 1}
        if  jsondata['TestStatus'] == 1:
            print('here')
            asyncio.create_task(self.discordmsg("SOMETHING WENT WRONG"))
            taskervar.stopprocess()
            return
        elif jsondata['TestStatus'] == 2:
            print('test')
            hora = converteHora(datetime.now(),True)
            asyncio.create_task(self.discordmsg(str("Everything is fine but not now: " + hora )))
            print('test2')

            
def converteHora(dateUTC, current=False):
    if current:
        response = (dateUTC.strftime("%d/%m/%Y, %H:%M:%S"))
    else:
        response = (dateutil.parser.isoparse(dateUTC)-timedelta(hours=3)).strftime("%d/%m/%Y, %H:%M:%S")
    return response

async def main():
    TOKEN = 'TOKEN GOES HERE'
    tasker()
    await asyncio.gather(
        await Helper().start(TOKEN)
    )


if __name__ == '__main__':
    asyncio.run(main())

Tags: nameimportselfhelperasynciodatetimeifdef
1条回答
网友
1楼 · 发布于 2024-06-17 10:27:48

您的主要问题是您没有授予辅助线程访问asyncio事件循环的权限。您不能只等待和/或create_task全局对象上的协同路由(首先避免使用全局对象的原因之一)。以下是如何修改代码以实现此目的:

class tasker: 
   def __init__(self):
      # ...
      self.loop = asyncio.get_running_loop()
    # ...

class getInfo:
    #...
    def requests(self):
        # replace the create_tasks calls with this. 
        asyncio.run_coroutine_threadsafe(self.discordmsg, taskervar.loop)

这将使用全局变量,因为我不想重写整个程序,但我仍然强烈建议避免使用它们,并考虑自己重新编写

尽管如此,我怀疑您仍会有以下缺陷:

If I dont run it on a different thread, however, it does work on the TestStatus: 1 but it makes Helper get stuck and stop running with TestStatus: 2

我不知道是什么导致了这个问题,我在我的机器上复制它时遇到了麻烦。您的代码很难阅读,并且缺少一些关于再现性的细节。我想这是你一开始没有得到答案的部分原因。我相信您已经了解了这篇文章,但可能值得再次访问,以获得共享代码的更好实践https://stackoverflow.com/help/minimal-reproducible-example

相关问题 更多 >