Unity AR应用蓝牙GPS读取卡顿及线程锁异常问题求助
问题:Unity AR应用蓝牙GPS读取卡顿与线程锁崩溃问题
我开发了一款使用ARLocation插件的Unity AR应用,使用设备自身GPS时运行正常,但连接发送高精度定位的蓝牙设备后,相机出现卡顿,卡顿恰好发生在ReadLine读取蓝牙数据的代码行。定位需要每帧更新,推测可能是蓝牙设备数据发送频率不足。我尝试将读取逻辑放入后台线程并使用锁,却触发了“Other thread trapped, signum = 6”错误,多次调整锁的实现仍未解决。
相关代码片段
1. Update帧更新逻辑
public virtual void Update() { if (!HasStarted) { return; } var location = ReadLocation(); var heading = ReadHeading(); if (location == null || heading == null) { // Debug.Log("[AbstractLocationProvider]: Null reading"); return; } if (FirstReading) { FirstLocation = location.Value; CurrentLocation = FirstLocation; CurrentLocationRaw = FirstLocation; CurrentHeading = heading.Value; IsEnabled = true; FirstReading = false; LocationEnabled?.Invoke(); InnerOnEnabled(); EmitCompassUpdated(); EmitLocationUpdated(); EmitLocationUpdatedRaw(); return; } UpdateLocation(location.Value); UpdateHeading(heading.Value); }
2. GPS数据读取解析逻辑
protected override LocationReading? ReadLocation() { if (!HasStarted) { return null; } if (Base.blAdapter.reader != null) { lock (Base.locker) { double längenString = Double.Parse(Base.lastGGAMessage.Split(",")[2].Replace(".", ",")) / 100; //I read the gps string here double grad = Math.Floor(längenString); double minuten = längenString - grad; double dezimalminuten = minuten / 0.6; double lat = dezimalminuten + grad; längenString = Double.Parse(Base.lastGGAMessage.Split(",")[4].Replace(".", ",")) / 100; grad = Math.Floor(längenString); minuten = längenString - grad; dezimalminuten = minuten / 0.6; double lng = dezimalminuten + grad; return new LocationReading() { latitude = lat, longitude = lng, altitude = double.Parse(Base.lastGGAMessage.Split(",")[9].Replace(".", ",")), accuracy = Base.HRMS, floor = -1, timestamp = (long)(Time.time * 1000) }; } } else { var data = Input.location.lastData; return new LocationReading() { latitude = data.latitude, longitude = data.longitude, altitude = data.altitude, accuracy = data.horizontalAccuracy, floor = -1, timestamp = (long)(Time.time * 1000) }; } }
3. 蓝牙后台线程读取逻辑
public static void ReadMessageToEnd(System.Object o) { while (Base.isConnected) { string line = null; try { line = Base.blAdapter.reader.ReadLine(); } catch (Exception) { return; } if (line.Length > 0) { switch (line.Split(",")[0]) { case "$GPGGA": lock (Base.locker) { Base.lastGGAMessage = line; //this is the important line } if (line.Split(",")[6].Equals("0")) { Base.invalidFix = true; } else { Base.invalidFix = false; } break; case "@GEPOSE": Base.gNeigungData = line; break; case "$GPGST": try { Base.VRMS = Math.Round(double.Parse(line.Split(",")[8].Replace(".", ",")), 2); Base.HRMS = Math.Round(Math.Sqrt(Math.Pow(double.Parse(line.Split(",")[6].Replace(".", ",")), 2) + Math.Pow(double.Parse(line.Split(",")[7].Replace(".", ",")), 2)), 2); } catch (Exception) { } break; default: break; } } } }
Unity LogCat崩溃日志
2023.02.06 14:42:07.596 20304 21788 Error CRASH other thread is trapped; signum = 6 0001.01.01 00:00:00.000 -1 -1 Info --------- beginning of crash 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime FATAL EXCEPTION: UnityMain 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime Process: com.ld2.LD2Survey, PID: 20304 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime java.lang.Error: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime Version '2021.3.17f1 (3e8111cac19d)', Build type 'Development', Scripting Backend 'il2cpp', CPU 'arm64-v8a' 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime Build fingerprint: 'realme/RMX2075EEA/RMX2075L1:12/RKQ1.211103.002/Q.GDPR.202211032034:user/release-keys' 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime Revision: '0' 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime ABI: 'arm64' 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime Timestamp: 2023-02-06 14:42:07+0100 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime pid: 20304, tid: 22085, name: Thread-58 >>> com.ld2.LD2Survey <<< 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime uid: 10544 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr -------- 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime x0 0000000000000000 x1 0000000000005645 x2 0000000000000006 x3 0000007a2d184e60 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime x4 fefefefefefefeff x5 fefefefefefefeff x6 fefefefefefefeff x7 7f7f7f7f7f7f7f7f 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime x8 00000000000000f0 x9 c77d01e1ee66ea5d x10 0000000000000000 x11 ffffff80fffffbdf 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime x12 0000000000000001 x13 00000000000000de x14 0000007a2d183c60 x15 ffffffffffffffff 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime x16 0000007d135d0bc0 x17 0000007d135ab680 x18 0000007a2c680000 x19 0000000000004f50 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime x20 0000000000005645 x21 00000000ffffffff x22 0000007c80815000 x23 0000000000000000 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime x24 0000007a2d185038 x25 0000007a2d188000 x26 0000007a2d188000 x27 0000000000000001 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime x28 000000000000004c x29 0000007a2d184ee0 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime sp 0000007a2d184e40 lr 0000007d1355b99c pc 0000007d1355b9c8 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime backtrace: 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime #00 pc 00000000000799c8 /apex/com.android.runtime/lib64/bionic/libc.so (abort+168) (BuildId: bbbdeb7c87c74f1491f92c6e605095b0) 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime 2023.02.06 14:42:07.597 20304 21788 Error AndroidRuntime at libc.abort(abort:168)
问题分析与修复方案
1. 缩小锁范围,避免主线程长时间持有锁
当前ReadLocation中把整个GPS解析逻辑都包裹在锁内,导致主线程(Update调用)长时间占用锁,后台线程无法及时更新共享变量,甚至引发死锁。仅在读写共享变量时加锁,解析逻辑放到锁外:
protected override LocationReading? ReadLocation() { if (!HasStarted) { return null; } if (Base.blAdapter.reader != null) { string ggaMessage = null; double currentHRMS = Base.HRMS; // 仅读取共享变量时加锁,缩小锁持有时间 lock (Base.locker) { ggaMessage = Base.lastGGAMessage; currentHRMS = Base.HRMS; } if(string.IsNullOrEmpty(ggaMessage)) return null; try { var split = ggaMessage.Split(","); double längenString = Double.Parse(split[2].Replace(".", ",")) / 100; double grad = Math.Floor(längenString); double minuten = längenString - grad; double dezimalminuten = minuten / 0.6; double lat = dezimalminuten + grad; längenString = Double.Parse(split[4].Replace(".", ",")) / 100; grad = Math.Floor(längenString); minuten = längenString - grad; dezimalminuten = minuten / 0.6; double lng = dezimalminuten + grad; return new LocationReading() { latitude = lat, longitude = lng, altitude = double.Parse(split[9].Replace(".", ",")), accuracy = currentHRMS, floor = -1, timestamp = (long)(Time.time * 1000) }; } catch(Exception ex) { Debug.LogError($"GPS数据解析失败: {ex.Message}"); return null; } } else { var data = Input.location.lastData; return new LocationReading() { latitude = data.latitude, longitude = data.longitude, altitude = data.altitude, accuracy = data.horizontalAccuracy, floor = -1, timestamp = (long)(Time.time * 1000) }; } }
2. 完善后台线程的线程安全与异常处理
后台线程中所有共享变量的读写都需要加锁,同时补充异常处理避免线程意外退出:
public static void ReadMessageToEnd(System.Object o) { while (Base.isConnected) { string line = null; try { line = Base.blAdapter.reader.ReadLine(); } catch (IOException ex) { Debug.LogError($"蓝牙读取失败: {ex.Message}"); break; } catch (Exception ex) { Debug.LogError($"未知读取错误: {ex.Message}"); continue; } if (string.IsNullOrEmpty(line)) continue; try { var split = line.Split(","); if(split.Length == 0) continue; switch (split[0]) { case "$GPGGA": lock (Base.locker) { Base.lastGGAMessage = line; } Base.invalidFix = split[6].Equals("0"); break; case "@GEPOSE": lock(Base.locker) { Base.gNeigungData = line; } break; case "$GPGST": if(split.Length <9) break; try { double vrms = Math.Round(double.Parse(split[8].Replace(".", ",")), 2);
相关产品推荐
相关产品推荐

