Exécution de run_coroutine_threadsafe dans un thread séparé
J'ai un script qui s'exécute constamment pour toujours (il vérifie les changements dans les fichiers). J'ai besoin d'envoyer des messages Discord chaque fois qu'un fichier étrange est créé.
- Le problème est que la fonction d'observation des événements (
def run(self):ci-dessous) provient d'une sous-classe, je ne peux donc pas la changer enasync def run(self):. Par conséquent, je ne peux pas utiliserawait channel.send() - Ma solution à cela était d'utiliser
run_coroutine_threadsafecomme expliqué ici:https://stackoverflow.com/a/53726266/9283107. Cela fonctionne bien! Mais le problème est que les messages sont placés dans une file d'attente et ne sont jamais envoyés tant que ce script n'est pas terminé (ce qui dans mon cas serait: jamais). Je suppose que les fonctions d'envoi de message sont placées dans le fil de discussion sur lequel ce script est activé, donc le fil n'y parvient jamais?
Peut-être pouvons-nous jeter le run_coroutine_threadsafedans un fil séparé ou quelque chose? C'est l'exemple le plus minimal que je puisse faire qui montre encore mon problème de sous-classe.
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')
Réponses
Tout d'abord, notez que vous n'êtes pas autorisé à appeler un code de blocage tel que time.sleep()depuis un fichier async def. Pour démarrer une fonction de blocage et la faire communiquer avec asyncio, vous pouvez créer un thread d'arrière-plan depuis on_readyou même depuis le niveau supérieur, comme ceci:
# 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()
Votre thread principal exécutera la boucle d'événements asyncio et votre thread d'arrière-plan vérifiera les fichiers, en utilisant asyncio.run_coroutine_threadsafe()pour communiquer avec asyncio et discord.
Comme indiqué dans un commentaire sous la réponse à laquelle vous avez lié, asyncio.run_coroutine_threadsafesuppose que vous avez plusieurs threads en cours d'exécution (donc "thread-safe"), dont l'un exécute la boucle d'événements. Jusqu'à ce que vous implémentiez cela, toute tentative d'utilisation asyncio.run_coroutine_threadsafeéchouera.
Suite aux user4815162342commentaires sur la question, je suis venu avec ceci, qui fonctionne parfaitement!
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')