【问题标题】:Calling python asyncio loop.run_until_complete() with discord.py not working?使用 discord.py 调用 python asyncio loop.run_until_complete() 不起作用?
【发布时间】:2018-04-02 14:10:08
【问题描述】:

使用 discord.py 制作 Discord 机器人,这是我第一次使用 asyncio,也可能是我第一次在 Python 中遇到这种令人沮丧的事情。

这个问题的重点不是教我如何使用 asyncio,而是教我如何避免使用它,即使它不是正确的做事方式。

所以我需要从常规的 def 函数运行不和谐的客户端协程。经过数小时的搜索,我发现了这个:asyncio.get_event_loop().run_until_complete(...)。我设置了一个小脚本来测试它:

import asyncio

async def test():
    print('Success')

asyncio.get_event_loop().run_until_complete(test())

而且效果很好。所以我继续尝试在不和谐的机器人中使用它:

import discord
import asyncio

client = discord.Client()

@client.event
async def on_ready():
    test()

def test():
    asyncio.get_event_loop().run_until_complete(run())

async def run():
    print('Success')

client.run('TOKEN_HERE')

我得到一个错误...堆栈跟踪/输出:

Success
Ignoring exception in on_ready
Traceback (most recent call last):
  File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 307, in _run_event
    yield from getattr(self, event)(*args, **kwargs)
  File "C:/Users/OverclockedSanic/PyCharm Projects/asyncio test/test.py", line 8, in on_ready
    test()
  File "C:/Users/OverclockedSanic/PyCharm Projects/asyncio test/test.py", line 11, in test
    asyncio.get_event_loop().run_until_complete(run())
  File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 454, in run_until_complete
    self.run_forever()
  File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 408, in run_forever
    raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running

奇怪的是最后的“成功”部分......我尝试了其他一些测试,看看我是否可以从协程返回数据或执行更多的东西,但它不能。

我什至尝试用client.loop 替换asyncio.get_event_loop(),但也没有用。

我找了 2 天,仍然没有解决方案。有什么想法吗?

编辑:按照 cmets 中的建议将 get_event_loop() 替换为 new_event_loop() 提出了以下问题:

Ignoring exception in on_ready
Traceback (most recent call last):
  File "C:\Program Files\Python36\lib\site-packages\discord\client.py", line 307, in _run_event
    yield from getattr(self, event)(*args, **kwargs)
  File "C:/Users/USER/PyCharm Projects/asyncio test/test.py", line 8, in on_ready
    test()
  File "C:/Users/USER/PyCharm Projects/asyncio test/test.py", line 11, in test
    asyncio.new_event_loop().run_until_complete(run())
  File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 454, in run_until_complete
    self.run_forever()
  File "C:\Program Files\Python36\lib\asyncio\base_events.py", line 411, in run_forever
    'Cannot run the event loop while another loop is running')
RuntimeError: Cannot run the event loop while another loop is running

【问题讨论】:

  • 查看examples on the github page。需要注意的重要一点是,您不必直接处理事件循环,discord.py 模块将为您处理这些事情
  • 我发现的唯一有趣的东西是 background_task.py,甚至没有太大帮助......
  • 如果你不想让test 成为协程,你需要在一个单独的循环中运行你的代码。 asyncio.new_event_loop

标签: python python-3.x python-asyncio discord discord.py


【解决方案1】:

您的问题似乎本质上是关于混合同步和异步代码。有两种可能:

1) 如果你的非异步例程不需要阻塞,只是为了安排一些异步任务(例如send_message)稍后运行,那么他们可以简单地调用get_event_loop().create_task()。如果您希望在异步操作完成时调用其他(非异步)例程,您甚至可以在返回的任务上使用add_done_callback。 (如果要运行的例程也是非异步的,则使用get_event_loop().call_soon()。)

2) 如果您的非异步例程绝对必须阻塞(包括可能等待异步例程),并且无法安排稍后的阻塞操作,那么您不应从与主事件循环相同的线程运行它们。您可以使用concurrent.futures.ThreadPoolExecutor 创建一个线程池,并使用asyncio.run_in_executor() 来安排您的非异步例程,然后等待结果。如果他们又需要调用异步例程,那么run_until_complete() 应该可以工作,因为现在您没有在已经有事件循环的线程中运行。 (但请注意线程安全问题。如果您需要等待主事件循环中的某些内容运行,您可能需要类似 run_coroutine_threadsafe 的内容。)

如果有帮助,asgiref 包包含可以为您简化此操作的例程。它们是为稍微不同的目的(Web 服务器)而设计的,但也可能对您有用。当您想从异步例程调用非异步例程时,可以使用await asgiref.sync.sync_to_async(func)(args),这将在线程池中运行该例程,然后在您想从非异步例程调用异步例程时使用asgiref.sync.async_to_sync(func)(args)它在那个线程池中运行。

【讨论】:

  • 我自己解决了这个问题,忘记了这个问题,但这似乎是一个有用的解决方案。
  • @OverclockedSanic,你能分享你的解决方案吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-12
  • 2018-10-04
  • 2021-02-25
  • 2018-07-04
  • 2021-06-04
  • 2021-08-11
  • 2021-01-02
相关资源
最近更新 更多