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

使用condition_variable搭配atomic_flag时,是否必须使用unique_lock<mutex>?

问题描述

我需要实现一个每10秒执行一次任务的std::jthread,同时允许在睡眠期间通过信号终止线程,写了如下代码:

void ThreadMain(std::condition_variable& cv, const std::atomic_flag& stop)
{
    [[maybe_unused]] std::mutex mtx;
    [[maybe_unused]] std::unique_lock<std::mutex> lock(mtx);
    for(;;)
    {
        DoSomeTask();
        if(cv.wait_for(lock, 10s, [&stop]() {
            return stop.test();
        }))
        {
            return;
        }
    }
}

int main()
{
    std::condition_variable cv;
    std::atomic_flag stop;
    std::jthread thread(&ThreadMain, std::ref(cv), std::cref(stop));
    ...
} 

终止线程时执行:

stop.test_and_set();
cv.notify_all();

我的疑问是:既然已经用了std::atomic_flag,完全不需要这个unique_lock<mutex>,但condition_variable必须绑定锁才能调用wait_for(目前标准里没有std::atomic_flag::wait_for,不然刚好适配这个场景)。我是不是遗漏了什么?当前标准下只能这么写,还是陷入了XY问题,有更简洁的实现方式?虽然这个没用到的std::mutex几乎没影响,但我还是很在意。

补充:我知道可以用jthread::request_stop和stop_token,但还是需要condition_variable(进而需要占位锁)来实现wait_for功能。

解决方案

当前C++标准里std::condition_variable确实必须搭配std::unique_lock<std::mutex>使用,哪怕这个锁只是占位用的。不过有几种更简洁的实现方式可以避免这种“无用锁”的情况:

方案1:原子变量+短轮询睡眠

不需要condition_variable,直接用std::this_thread::sleep_for结合原子变量的轮询检查,虽会有最多100ms的延迟(可调整),但实现简单:

void ThreadMain(const std::atomic_flag& stop)
{
    for(;;)
    {
        DoSomeTask();
        // 分100次短睡眠,每次检查终止信号
        for(int i = 0; i < 100; ++i)
        {
            if(stop.test())
                return;
            std::this_thread::sleep_for(100ms);
        }
    }
}

int main()
{
    std::atomic_flag stop = ATOMIC_FLAG_INIT;
    std::jthread thread(&ThreadMain, std::cref(stop));
    // 终止线程
    stop.test_and_set();
}

方案2:使用std::condition_variable_any+空锁

std::condition_variable_any支持绑定任何满足BasicLockable要求的类型,我们可以自定义一个空锁类型,避免创建实际的std::mutex:

struct NullLock
{
    void lock() {}
    void unlock() {}
};

void ThreadMain(std::condition_variable_any& cv, const std::atomic_flag& stop)
{
    NullLock mtx;
    std::unique_lock<NullLock> lock(mtx);
    for(;;)
    {
        DoSomeTask();
        if(cv.wait_for(lock, 10s, [&stop]() {
            return stop.test();
        }))
        {
            return;
        }
    }
}

int main()
{
    std::condition_variable_any cv;
    std::atomic_flag stop = ATOMIC_FLAG_INIT;
    std::jthread thread(&ThreadMain, std::ref(cv), std::cref(stop));
    // 终止线程
    stop.test_and_set();
    cv.notify_all();
}

方案3:结合std::stop_token与std::condition_variable_any

利用C++20的std::stop_token和std::stop_callback,搭配condition_variable_any实现无额外冗余锁的等待,完全复用jthread原生停止机制:

void ThreadMain(std::stop_token st)
{
    std::condition_variable_any cv;
    std::mutex mtx;
    std::unique_lock<std::mutex> lock(mtx);
    // 注册停止回调,触发时通知条件变量
    std::stop_callback cb(st, [&cv]() {
        cv.notify_all();
    });

    for(;;)
    {
        DoSomeTask();
        // 等待10秒或收到停止信号
        if(cv.wait_for(lock, 10s, [&st]() {
            return st.stop_requested();
        }))
        {
            return;
        }
    }
}

int main()
{
    std::jthread thread(&ThreadMain);
    // 终止线程
    thread.request_stop();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 05:07:41