Unity使用websocket-sharp时OnMessage并发消息导致MakeUILobby参数异常
问题根因
这是C# 闭包变量捕获的典型问题,叠加了WebSocket异步回调和Unity主线程调度的特性:
- 你声明的
WSUserJoinedToRoom是类级别的全局变量,每次WebSocket收到消息都会直接覆盖这个变量的存储值 - 你入队的Lambda表达式捕获的是
WSUserJoinedToRoom这个变量的引用,不是当前收到消息时的瞬时值 - 短时间内多条消息到达时,前一条消息的Lambda还没被执行,
WSUserJoinedToRoom就已经被后一条消息的解析结果覆盖,最终所有Lambda执行时读取的都是最后一次覆盖的值
修复方案
把解析结果存在WebSocket回调的局部变量中,让Lambda捕获每次独立的局部变量副本即可,修改后的代码如下:
public IEnumerator Start() { ws = new WebSocket("ws://localhost:8081"); ws.SetCookie(); ws.Connect(); WSConnectionStatus = true; ws.OnMessage += (sender, e) => { // 解析结果存入当前回调的局部变量,不要用全局变量中转 var currentUser = JsonUtility.FromJson<WSUserJoinedToRoom>(e.Data); print("Message Received from " + ((WebSocket)sender).Url + ", Data : " + e.Data); // Lambda捕获当前回调的独立局部变量,不会被后续回调覆盖 _actions.Enqueue(() => MakeUILobby(currentUser.user_id, currentUser.user_name)); }; } private void MakeUILobby(string user_id, string user_name) { print(user_name); } // 额外补充:你需要在Unity主线程生命周期函数中消费队列,否则入队的方法不会执行 private void Update() { while (_actions.Count > 0) { _actions.Dequeue()?.Invoke(); } }
修改后原来的类级别WSUserJoinedToRoom变量如果没有其他用途可以直接删除,避免后续再出现类似的变量覆盖问题。
内容的提问来源于stack exchange,提问作者Mohammad Nikouei
相关产品推荐
相关产品推荐

