Esecuzione di run_coroutine_threadsafe in un thread separato
Ho uno script che è costantemente in esecuzione per sempre (controlla le modifiche nei file). Devo inviare messaggi Discord ogni volta che viene creato un file strano.
- Il problema è che la funzione di visualizzazione degli eventi (
def run(self):sotto) proviene da una sottoclasse, quindi non posso cambiarla inasync def run(self):. Quindi non posso usareawait channel.send() - La mia soluzione a questo era usare
run_coroutine_threadsafecome spiegato qui:https://stackoverflow.com/a/53726266/9283107. Funziona bene! Ma il problema è che i messaggi vengono inseriti in una coda e non vengono mai inviati fino al termine di questo script (che nel mio caso sarebbe: mai). Presumo che le funzioni di invio del messaggio vengano inserite nel thread su cui si trova questo script, quindi il thread non le arriva mai?
Forse possiamo inserire il run_coroutine_threadsafein un thread separato o qualcosa del genere? Questo è l'esempio più minimale che posso fare che mostra ancora il mio problema di sottoclasse.
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')
Risposte
Innanzitutto, tieni presente che non puoi chiamare codice di blocco come time.sleep()da un file async def. Per avviare una funzione di blocco e farla comunicare con asyncio, puoi generare un thread in background da on_readyo anche dal livello superiore, come questo:
# 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()
Il thread principale eseguirà il ciclo di eventi asyncio e il thread in background controllerà i file, utilizzando asyncio.run_coroutine_threadsafe()per comunicare con asyncio e discord.
Come sottolineato in un commento sotto la risposta a cui ti sei collegato, asyncio.run_coroutine_threadsafepresume che tu abbia più thread in esecuzione (quindi "thread-safe"), uno dei quali esegue il ciclo di eventi. Fino a quando non lo implementerai, qualsiasi tentativo di utilizzo asyncio.run_coroutine_threadsafefallirà.
Dopo aver user4815162342commentato la domanda, mi è venuta in mente questo, che funziona perfettamente!
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')