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

将notify_one()置于锁定范围内能否避免线程竞争问题?

LockableOperationT竞态条件的解决疑问

我实现了一个包装类LockableOperationT,用于在请求方线程和执行操作的提供方线程之间处理同步操作:请求方线程调用waitUntilProcessed()等待操作完成,提供方线程执行process()完成操作后通知请求方。

原代码实现

template <class tBridgeDeclType>
class LockableOperationT : public OperationSignatureT<tBridgeDeclType>
{
public:
    explicit LockableOperationT(const OperationSignatureT<tBridgeDeclType> &fOperation) :
        mOperation(fOperation) { }
    virtual ~LockableOperationT() { }


    void waitUntilProcessed()
    {
        // Lock the bridge from the requiring side.
        std::unique_lock<std::mutex> processLock(mMutex);

        // Wait until operation has been processed and take care of spurious wake ups.
        mConditionVariable.wait(processLock, [this]{ return(mHasBeenProcessed); });
    }


    void process(void) override final
    {
        // Process operation. Have to remove const to do this
        const_cast<OperationSignatureT<tBridgeDeclType> &>(mOperation).process();

        // Lock the bridge from the providing side.
        {
            std::lock_guard<std::mutex> processLock(mMutex);
            mHasBeenProcessed = true;
        }

        // Requiring side is notified that the operation has been processed
        mConditionVariable.notify_one();
    }


// Members
protected:
    const OperationSignatureT<tBridgeDeclType> &mOperation;
    bool mHasBeenProcessed = false;
    std::mutex mMutex;
    std::condition_variable mConditionVariable;
};

执行流程

请求方发送操作给提供方后等待完成,提供方执行操作后通知请求方,请求方收到通知后销毁LockableOperationT对象,符合RAII原则。

原代码的竞态条件

  • 请求方线程(Thread-1)调用waitUntilProcessed(),加锁并等待mHasBeenProcessed为true;
  • 提供方线程(Thread-2)执行process(),设置mHasBeenProcessed为true后退出锁定范围;
  • Thread-1发生虚假唤醒,检测到mHasBeenProcessed为true后返回,请求方销毁LockableOperationT对象;
  • Thread-2继续执行,调用已销毁对象的mConditionVariable.notify_one(),引发未定义行为。

疑问

将notify_one()移动到锁定范围内(如下代码所示),能否避免该竞态条件,还是仅会将竞态转移到其他位置?

// Lock the bridge from the providing side.
{
    std::lock_guard<std::mutex> processLock(mMutex);
    mHasBeenProcessed = true;

    // Requiring side is notified that the operation has been processed
    mConditionVariable.notify_one();
}

解答

把notify_one()移到锁范围内确实能解决这个特定的竞态条件,不会转移到其他位置,核心原因如下:

  1. 锁的同步与内存可见性:当Thread-2在锁内完成mHasBeenProcessed = true和notify_one()的调用时,互斥锁会保证这些操作的内存可见性——Thread-1在wait返回时看到的mHasBeenProcessed状态一定是最新的。同时,wait在返回前会重新获取锁,这意味着Thread-1必须等到Thread-2释放锁之后,才能退出wait并执行后续的对象销毁逻辑。

  2. 彻底避免对象提前销毁:Thread-1只有在Thread-2完全释放锁(即已经完成锁内的所有操作,包括notify_one())之后,才会离开waitUntilProcessed()并销毁对象。这样就从根本上杜绝了Thread-2调用已销毁对象的notify_one()的情况。

另外需要注意:原代码中使用const_cast去除mOperation的const属性存在风险,如果传入的mOperation实际是const对象,修改它会触发未定义行为。建议调整mOperation的类型为非const引用,或者在设计层面确保传入的操作允许被修改。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 05:33:11