별도의 스레드에서 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여러 스레드가 실행 중이라고 가정합니다 (따라서 "스레드 안전").이 중 하나는 이벤트 루프를 실행합니다. 이를 구현할 때까지 모든 사용 시도 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')