多人游戏开发中如何不阻塞主程序实现线程到主程序的值传递?
非阻塞跨线程数据传递实现方案
你之前遇到队列、管道阻塞主线程的问题,本质是调用了默认的阻塞式读取接口,改用以下几种非阻塞方案即可解决:
方案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
相关产品推荐
相关产品推荐

