在单独的线程中执行run_coroutine_threadsafe [英] Executing run_coroutine_threadsafe in a separate thread

查看:543
本文介绍了在单独的线程中执行run_coroutine_threadsafe的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个永远不断运行的脚本(它检查文件中的更改)。每当生成一个奇怪的文件时,我都需要发送Discord消息。

I have a script that is constantly running forever (it checks changes in files). I need to send Discord messages whenever a weird file is made.


  • 问题是事件监视功能( def run(self) :下面是一个子类,因此我无法将其更改为 async def run(self):。因此,我不能使用 await channel.send()

  • 我的解决方案是使用 run_coroutine_threadsafe 就像这里的解释: https://stackoverflow.com/a/53726266/9283107 。很好!但是问题是,消息被放入队列中,直到该脚本完成(在我的情况下是:从不),它们才被发送出去。我假设将send message函数放入该脚本所在的线程中,因此该线程永远不会到达它们?

  • Problem is, the event watching function (def run(self): below) is from a subclass, so I can't change it to async def run(self):. Therefore I can't use await channel.send()
  • My solution to this was to use run_coroutine_threadsafe like explained here: https://stackoverflow.com/a/53726266/9283107. That works good! But the problem is, the messages get put into a queue and they never get sent until this script finishes (which in my case would be: never). I assume the send message functions get put into the thread that this script is on, therefore the thread never gets to them?

也许我们可以抛出 run_coroutine_threadsafe 进入单独的线程还是其他东西?这是我可以做的最简单的例子,仍然显示我的子类问题。

Maybe we can throw the run_coroutine_threadsafe into a separate thread or something? This is the most minimal example I can make that still shows my subclass problem.

import discord
import os
import asyncio
import time

# CHANNEL_ID = 7659170174????????
client = discord.Client()
channel = None

class Example():
    # Imagine this run comes from a subclass, so you can't add sync to it!
    def run(self):
        # await channel.send('Test') # We can't do this because of the above comment
        asyncio.run_coroutine_threadsafe(channel.send('Test'), _loop)
        print('Message sent')

@client.event
async def on_ready():
    print('Discord ready')
    global channel
    channel = client.get_channel(CHANNEL_ID)

    for i in range(2):
        Example().run()
        time.sleep(3)

    print('Discord messages should appear by now. Sleeping for 20s to give it time (technically this would be infinite)')
    time.sleep(20)
    print('Script done. Now they only get sent for some reason')

_loop = asyncio.get_event_loop()

client.run('Your secret token')


推荐答案

首先,请注意,您不能从 async def 调用阻止代码,例如 time.sleep()。要启动阻止功能并使其与asyncio通信,您可以从 on_ready 甚至从顶层生成后台线程,如下所示:

First, note that you're not allowed to call blocking code such as time.sleep() from an async def. To start a blocking function and have it communicate with asyncio, you can spawn a background thread from on_ready or even from top-level, like this:

# checker_function is the function that blocks and that
# will invoke Example.run() in a loop.
threading.Thread(
    target=checker_function,
    args=(asyncio.get_event_loop(), channel)
).start()

您的主线程将运行asyncio事件循环,而您的后台线程将使用 asyncio.run_coroutine_threadsafe()与asyncio和不和谐通信。

Your main thread will run the asyncio event loop and your background thread will check the files, using asyncio.run_coroutine_threadsafe() to communicate with asyncio and discord.

正如您在链接到的答案下方的注释中指出的那样, asyncio.run_coroutine_threadsafe 假定您有多个线程正在运行(因此称为线程安全),其中一个运行事件循环。在执行该操作之前,任何使用 asyncio.run_coroutine_threadsafe 的尝试都会失败。

As pointed out in a comment under the answer you linked to, asyncio.run_coroutine_threadsafe assumes that you have multiple threads running (hence "thread-safe"), one of which runs the event loop. Until you implement that, any attempt to use asyncio.run_coroutine_threadsafe will fail.

这篇关于在单独的线程中执行run_coroutine_threadsafe的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆