You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多人游戏开发中如何不阻塞主程序实现线程到主程序的值传递?

非阻塞跨线程数据传递实现方案

你之前遇到队列、管道阻塞主线程的问题,本质是调用了默认的阻塞式读取接口,改用以下几种非阻塞方案即可解决:

方案1:使用queue.Queue非阻塞读取(最通用,无额外依赖)

标准库的queue.Queue本身支持非阻塞读写,只需要调整调用参数即可:

  • 初始化不设最大长度的共享Queue实例,网络接收线程收到位置数据后调用put()写入,无上限队列的put()方法默认完全非阻塞,不会卡住接收线程
  • 在Ursina的update()函数中循环调用get(block=False)读取所有积压的新数据,捕获queue.Empty异常终止读取,整个过程无任何等待,不会阻塞渲染逻辑
    示例代码:
import queue
from ursina import *

# 全局共享队列
position_queue = queue.Queue()
other_player = Entity(model='cube', color=color.red)

# 独立线程运行的网络接收逻辑
def network_receive_task():
    while True:
        # 这里是你的socket接收逻辑,仅阻塞当前接收线程
        new_player_pos = receive_pos_from_server()
        position_queue.put(new_player_pos)

# 运行在主线程的帧更新函数
def update():
    # 一次性处理所有新收到的位置数据,无数据时立刻退出循环
    while True:
        try:
            pos = position_queue.get(block=False)
            other_player.position = pos
        except queue.Empty:
            break

方案2:使用Ursina内置invoke调度(代码侵入最小)

Ursina提供的invoke方法可以直接将函数调度到主线程的下一个更新周期执行,不需要手动维护队列和处理线程安全:

  • 网络接收线程拿到位置数据后,直接调用invoke(更新函数, 参数)即可,更新逻辑会自动在主线程执行
    示例代码:
from ursina import invoke, Entity, color

other_player = Entity(model='cube', color=color.red)

# 独立线程运行的网络接收逻辑
def network_receive_task():
    while True:
        new_player_pos = receive_pos_from_server()
        # 调度更新逻辑到主线程执行
        invoke(update_other_player_pos, new_player_pos)

# 主线程执行的位置更新函数
def update_other_player_pos(new_pos):
    other_player.position = new_pos

方案3:无锁环形缓冲区(高更新频率场景可选)

如果玩家位置更新频率极高(每秒超过100次),可以用无锁环形缓冲区进一步降低开销:

  • 预分配固定长度的列表作为缓冲区,用threading.AtomicInt分别维护读、写指针
  • 接收线程写入数据后移动写指针,主线程update时批量读取读指针到写指针之间的所有新数据后移动读指针即可,全程无锁无阻塞

注意事项

  • 所有网络IO、阻塞式操作必须放在独立线程执行,不要在主线程调用任何可能等待的接口
  • 位置数据建议只传增量更新,减少主线程的计算开销

内容的提问来源于stack exchange,提问作者Druide

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 11:45:03