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

多线程BLE地址获取函数cv.notify_all()触发访问违规错误排查

错误根因分析

  • 核心问题:函数栈变量生命周期和回调生命周期不匹配
    你代码中用到的std::mutex m、std::condition_variable cv、addr、stopCalled、advWatcher都是GetFirstAdvertisingBLEAddr函数的栈上局部变量。两个事件回调(Received、Stopped)都是按引用捕获这些栈变量,而WinRT的BLE事件回调是异步执行的:当cv.wait条件满足返回后,函数会立刻开始销毁栈上的局部变量,此时如果回调还在执行(甚至还没开始调度执行),访问已经被销毁的cv、mutex等对象就会触发"访问违规"错误,你报错的cv.notify_all()行就是刚好访问到了已析构的cv对象。
  • 其他多线程问题
    1. 共享变量存在数据竞争:Received回调中写addr时没有加锁,和wait谓词中读addr的操作构成数据竞争,属于C++未定义行为。
    2. 回调移除时机过晚:你在wait返回后才移除事件回调,此时已经有处于调度中、未执行的回调无法被拦截,依然会访问已销毁的栈变量。

修复方案

  1. 把所有跨线程共享的状态封装到堆上,用std::shared_ptr持有,让回调的生命周期和共享状态的生命周期绑定,避免提前销毁:
#include <memory>
#include <mutex>
#include <condition_variable>

using namespace winrt::Windows::Devices::Bluetooth;
using namespace winrt::Windows::Devices::Bluetooth::Advertisement;

struct BleWatchState
{
    std::mutex mtx;
    std::condition_variable cv;
    uint64_t foundAddr = 0;
    bool isStopped = false;
};

uint64_t GetFirstAdvertisingBLEAddr()
{
    auto state = std::make_shared<BleWatchState>();
    BluetoothLEAdvertisementWatcher advWatcher;

    auto recvToken = advWatcher.Received([state, advWatcher](BluetoothLEAdvertisementWatcher const&, BluetoothLEAdvertisementReceivedEventArgs const& eventArgs) {
        ShowAdvertisement(eventArgs);
        // 加锁访问共享变量,避免重复处理
        {
            std::unique_lock l(state->mtx);
            if (state->foundAddr != 0) return;
            state->foundAddr = eventArgs.BluetoothAddress();
        }
        advWatcher.Stop();
        std::unique_lock l(state->mtx);
        state->cv.notify_all();
    });

    auto stoppedToken = advWatcher.Stopped([state](BluetoothLEAdvertisementWatcher const&, BluetoothLEAdvertisementWatcherStoppedEventArgs const&) {
        std::unique_lock l(state->mtx);
        state->isStopped = true;
        state->cv.notify_all();
    });

    uint64_t result = 0;
    {
        std::unique_lock l(state->mtx);
        advWatcher.Start();
        state->cv.wait(l, [&] { 
            return state->foundAddr != 0 && state->isStopped 
                && (advWatcher.Status() == BluetoothLEAdvertisementWatcherStatus::Aborted 
                    || advWatcher.Status() == BluetoothLEAdvertisementWatcherStatus::Stopped); 
        });
        result = state->foundAddr;
    }

    // 先停止再移除回调
    if (advWatcher.Status() == BluetoothLEAdvertisementWatcherStatus::Started)
    {
        advWatcher.Stop();
    }
    advWatcher.Received(recvToken);
    advWatcher.Stopped(stoppedToken);

    return result;
}
  1. 额外优化点:如果需要处理超时场景,可以把cv.wait换成cv.wait_for,避免设备未扫描到时函数永久阻塞。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.14 05:45:02