如何在.NET Core中用游戏循环触发SignalR群组消息?
.NET Core SignalR 实现GameRoom定时自动广播方案
核心替代方案
在.NET Core SignalR中,不再使用GlobalHost获取Hub上下文,而是通过依赖注入(DI)注入IHubContext<GameHub>,让GameRoom直接持有上下文实例来发送群组消息,这是符合.NET Core设计规范的标准做法。
具体实现步骤与代码示例
1. 重构GameRoom,注入IHubContext
将IHubContext<GameHub>通过构造函数注入到GameRoom中,替代原来的GlobalHost方式。同时推荐使用.NET 6+引入的PeriodicTimer(异步友好,避免传统Timer的线程问题)实现5秒循环:
public class GameRoom { private readonly IHubContext<GameHub> _hubContext; private readonly List<Player> _players; private readonly Scoreboard _scores; private readonly string _gameRoomName; private PeriodicTimer _gameLoopTimer; // 构造函数注入IHubContext public GameRoom(IHubContext<GameHub> hubContext, string gameRoomName) { _hubContext = hubContext; _gameRoomName = gameRoomName; _players = new List<Player>(); _scores = new Scoreboard(); } // 启动异步游戏循环 public async Task StartGameLoopAsync(CancellationToken cancellationToken = default) { _gameLoopTimer = new PeriodicTimer(TimeSpan.FromSeconds(5)); try { while (await _gameLoopTimer.WaitForNextTickAsync(cancellationToken)) { // 组装当前游戏状态数据 var gameState = GetCurrentGameState(); // 向对应群组广播消息 await _hubContext.Clients.Group(_gameRoomName).SendAsync("ReceiveGameState", gameState); } } catch (OperationCanceledException) { // 处理房间关闭时的取消逻辑 } finally { // 释放Timer资源 await _gameLoopTimer.DisposeAsync(); } } // 停止游戏循环 public void StopGameLoop() { _gameLoopTimer?.Dispose(); } // 获取当前游戏状态(自定义实现) private GameState GetCurrentGameState() { return new GameState { Scoreboard = _scores, Players = _players, RoomName = _gameRoomName }; } }
2. 在GameHub中管理GameRoom
在GameHub中通过DI获取IHubContext<GameHub>,创建GameRoom时将上下文传递进去,并处理房间的生命周期:
public class GameHub : Hub { private readonly IHubContext<GameHub> _hubContext; private readonly List<GameRoom> _gameRooms; // 注入IHubContext用于创建GameRoom public GameHub(IHubContext<GameHub> hubContext) { _hubContext = hubContext; _gameRooms = new List<GameRoom>(); } // 示例:创建游戏房间的方法 public async Task CreateGameRoom(string roomName) { var existingRoom = _gameRooms.FirstOrDefault(r => r.RoomName == roomName); if (existingRoom != null) { await Clients.Caller.SendAsync("RoomExists", roomName); return; } var newRoom = new GameRoom(_hubContext, roomName); _gameRooms.Add(newRoom); // 将当前连接加入房间群组 await Groups.AddToGroupAsync(Context.ConnectionId, roomName); // 启动房间的定时广播循环 await newRoom.StartGameLoopAsync(); await Clients.Caller.SendAsync("RoomCreated", roomName); } // 示例:玩家退出房间时的清理逻辑 public override async Task OnDisconnectedAsync(Exception exception) { // 找到当前连接所在的房间,处理玩家退出、房间销毁等逻辑 // ... await base.OnDisconnectedAsync(exception); } }
关键说明
- 异步优先:使用
PeriodicTimer配合async/await,避免传统System.Timers.Timer的线程池线程问题,更适配.NET Core的异步生态。 - 生命周期管理:房间关闭或玩家全部退出时,务必调用
StopGameLoop释放PeriodicTimer,防止内存泄漏。 - 依赖注入优势:通过DI获取
IHubContext,符合.NET Core的控制反转原则,便于测试和扩展。
内容的提问来源于stack exchange,提问作者jmanghera
相关产品推荐
相关产品推荐

