別のスレッドでrun_coroutine_threadsafeを実行する
Oct 15 2020
私は常に永久に実行されているスクリプトを持っています(ファイルの変更をチェックします)。奇妙なファイルが作成されるたびにDiscordメッセージを送信する必要があります。
- 問題は、イベント監視関数(
def run(self):以下)がサブクラスからのものであるため、に変更できないことasync def run(self):です。だから使えないawait channel.send() - これに対する私の解決策は、
run_coroutine_threadsafeここで説明されているように使用することでした:https://stackoverflow.com/a/53726266/9283107。それはうまくいきます!しかし、問題は、メッセージがキューに入れられ、このスクリプトが終了するまでメッセージが送信されないことです(私の場合は、決して送信されません)。メッセージ送信関数は、このスクリプトが実行されているスレッドに配置されると思います。したがって、スレッドはそれらに到達しませんか?
たぶんrun_coroutine_threadsafe、別のスレッドか何かに投げることができますか?これは私が作ることができる最も最小限の例であり、それでも私のサブクラスの問題を示しています。
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')
回答
2 user4815162342 Oct 15 2020 at 11:05
まず、time.sleep()からなどのブロッキングコードを呼び出すことは許可されていないことに注意してくださいasync def。ブロッキング関数を開始して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()
メインスレッドはasyncioイベントループを実行し、バックグラウンドスレッドはasyncio.run_coroutine_threadsafe()asyncioおよびdiscordとの通信に使用してファイルをチェックします。
リンクした回答の下のコメントで指摘されているように、asyncio.run_coroutine_threadsafe複数のスレッドが実行されていることを前提としています(したがって、「スレッドセーフ」)。そのうちの1つがイベントループを実行します。それを実装するまで、使用しようとしてasyncio.run_coroutine_threadsafeも失敗します。
2 Frank Oct 15 2020 at 10:59
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')