简体   繁体   English

在单独的线程中执行 run_coroutine_threadsafe

[英]Executing run_coroutine_threadsafe in a separate thread

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.每当制作奇怪的文件时,我都需要发送 Discord 消息。

  • 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): .问题是,事件监视函数( def run(self):下面)来自一个子类,所以我无法将其更改为async def run(self): Therefore I can't use await channel.send()因此我不能使用await channel.send()
  • My solution to this was to use run_coroutine_threadsafe like explained here: https://stackoverflow.com/a/53726266/9283107 .我对此的解决方案是使用run_coroutine_threadsafe就像这里解释的那样: https : run_coroutine_threadsafe 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?我假设发送消息函数被放入这个脚本所在的线程中,因此线程永远不会到达它们?

Maybe we can throw the run_coroutine_threadsafe into a separate thread or something?也许我们可以将run_coroutine_threadsafe扔到一个单独的线程中? 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')

First, note that you're not allowed to call blocking code such as time.sleep() from an async def .首先,请注意,不允许从async def调用阻塞代码,例如time.sleep() 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:要启动一个阻塞函数并让它与 asyncio 通信,你可以从on_ready甚至顶级生成一个后台线程,如下所示:

# 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()

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 事件循环,您的后台线程将检查文件,使用asyncio.run_coroutine_threadsafe()与 asyncio 和 discord 进行通信。

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.正如您链接到的答案下的评论中指出的那样, asyncio.run_coroutine_threadsafe假设您有多个线程正在运行(因此是“线程安全的”),其中一个运行事件循环。 Until you implement that, any attempt to use asyncio.run_coroutine_threadsafe will fail.在你实现它之前,任何使用asyncio.run_coroutine_threadsafe尝试asyncio.run_coroutine_threadsafe将失败。

Following user4815162342 comments on the question, I came up with this, which works perfectly!按照user4815162342对该问题的评论,我想出了这个,效果很好!

import discord
import os
import asyncio
import time
import threading

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('Tester'), _loop)
        print('Message sent')

def start_code():
    for i in range(2):
        Example().run()
        time.sleep(20)

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

    threading.Thread(target=start_code).start()

_loop = asyncio.get_event_loop()

client.run('Your secret token')

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM