如何在线程内启动异步目标

Posted

技术标签:

【中文标题】如何在线程内启动异步目标【英文标题】:How to start async target inside thread 【发布时间】:2021-06-14 21:49:41 【问题描述】:

我正在使用 pyCraft 库与 discord 和 minecraft 建立桥梁,我一直在尝试多种解决方案,例如 asyncio loop thing,但运行时它只运行 minecraft 客户端,并没有启动 discord 客户端直到我做一个键盘中断。我能弄清楚这如何工作的唯一方法是使用线程。此函数需要异步,因为它使用await ctx.send 方法发送到通道。我还需要不和谐并且该功能能够相互交互。异步:

import discord
import asyncio
from minecraft import authentication
from minecraft.exceptions import YggdrasilError
from minecraft.networking.connection import Connection
from minecraft.networking.packets import Packet, clientbound, serverbound

client = discord.Client()

async def mcClient(server='mc.hypixel.net', username='minecraft email', password='password', *, port=25565, version=340):
    # version 340 - Minecraft 1.12.2
    if password is None:
        print("Connecting in offline mode...")
        connection = Connection(
            server, port, username=username)
    else:
        auth_token = authentication.AuthenticationToken()
        try:
            auth_token.authenticate(username, password)
        except YggdrasilError as e:
            print(e)
            sys.exit()
        print("(Online) Logged in as %s..." % auth_token.username)
        connection = Connection(
            server, port, auth_token=auth_token, initial_version=version)

    # listener on join game
    def handle_join_game(join_game_packet):
        print('Connected.')
    
    connection.register_packet_listener(
        handle_join_game, clientbound.play.JoinGamePacket)
    
    # print chat
    def handle_chat(chat_packet):
        cpke = chat_packet.json_data
        print(f"Chat: cpke")
        channel = client.get_channel(channel_id)
        await channel.send(cpke)

    connection.register_packet_listener(
        handle_chat, clientbound.play.ChatMessagePacket)

    connection.connect()

    # listen for input and send message
    while True:
        try:
            text = input()
            packet = serverbound.play.ChatPacket()
            packet.message = text
            connection.write_packet(packet)
        except KeyboardInterrupt:
            print("Exiting client.")
            sys.exit()

asyncio.get_event_loop().create_task(mcClient())
client.run(TOKEN)

线程(无异步):

import discord
import threading
from minecraft import authentication
from minecraft.exceptions import YggdrasilError
from minecraft.networking.connection import Connection
from minecraft.networking.packets import Packet, clientbound, serverbound

client = discord.Client()

def mcClient(server='mc.hypixel.net', username='minecraft email', password='password', *, port=25565, version=340):
    # version 340 - Minecraft 1.12.2
    if password is None:
        print("Connecting in offline mode...")
        connection = Connection(
            server, port, username=username)
    else:
        auth_token = authentication.AuthenticationToken()
        try:
            auth_token.authenticate(username, password)
        except YggdrasilError as e:
            print(e)
            sys.exit()
        print("(Online) Logged in as %s..." % auth_token.username)
        connection = Connection(
            server, port, auth_token=auth_token, initial_version=version)

    # listener on join game
    def handle_join_game(join_game_packet):
        print('Connected.')
    
    connection.register_packet_listener(
        handle_join_game, clientbound.play.JoinGamePacket)
    
    # print chat
    def handle_chat(chat_packet):
        cpke = chat_packet.json_data
        print(f"Chat: cpke")

    connection.register_packet_listener(
        handle_chat, clientbound.play.ChatMessagePacket)

    connection.connect()

    # listen for input and send message
    while True:
        try:
            text = input()
            packet = serverbound.play.ChatPacket()
            packet.message = text
            connection.write_packet(packet)
        except KeyboardInterrupt:
            print("Exiting client.")
            sys.exit()

x = threading.Thread(target=mcClient(), daemon=True)
x.start()
client.run(TOKEN)

虽然这可行,但我无法发送消息或做任何与不和谐相关的事情。任何帮助将不胜感激!

编辑:我把整个函数和子函数放在一起,以便于理解

【问题讨论】:

尝试在你调用的函数后面添加await 我不能等待它,因为线程没有在异步函数中启动。我尝试将它放在异步函数中,但它返回“无法等待函数对象” 【参考方案1】:

asyncio.gather 可能会有所帮助。

import discord
import asyncio
import pyCraft

client = discord.Client()

async def mcClient():
  # stuff
  global connection
  connection = pyCraft.connection(args)
  connection.connect()

async def handle_chat():
  # send message to discord here
  # because of the global statement, you can reference connection from here too!
  pass

asyncio.gather(mcClient(), handle_chat())
client.run(TOKEN)

【讨论】:

gather() 做了什么 @WindowsIsCool asyncio.gather 并行运行两个协程,以便 Discord 可以与 Minecraft 交互。 我对原始帖子进行了编辑,但我仍然不确定如何完全实现这一点,因为我需要定义一个侦听器。另外,我可以把函数变成一个类并调用它的子函数或类似的东西 你也不能用 () 来调用函数,否则它会在运行那一行时调用它

以上是关于如何在线程内启动异步目标的主要内容,如果未能解决你的问题,请参考以下文章

如何在Android开发中用AsyncTask异步更新UI界面

如何实现webservice的异步调用

第十次总结 线程的异步和同步

如何在 gRPC 异步服务中使用共享完成队列

C#如何在不等待完成的情况下启动异步方法?

如何使用异步 Web 请求进行多线程