c++ 11 std::atomic_flag,我用对了吗?

c++ 11 std::atomic_flag, am I using this correctly?

本文关键字:flag std atomic c++      更新时间:2023-10-16

我有一个简单的布尔值,我需要以线程安全的方式测试和设置。如果一个线程已经在工作,我希望第二个线程退出。如果我正确理解std::atomic_flag,这应该可以正常工作。然而,我不相信我正确理解std::atomic_flag:)我似乎找不到很多简单的例子在线,保存这个自旋锁的例子:

// myclass.cpp
#using <atomic>
namespace  // anonymous namespace
{
    std::atomic_flag _my_flag = ATOMIC_FLAG_INIT;
}  // ns
myclass::do_something()
{
    if ( !::_my_flag.test_and_set() ) )
    {
        // do my stuff here; handle errors and clear flag when done
        try
        {
            // do my stuff here
        }
        catch ( ... )
        {
            // handle exception
        }
        ::_my_flag.clear();  // clear my flag, we're done doing stuff
    }
    // else, we're already doing something in another thread, let's exit
}  // do_something

更新:根据以下建议更新代码,形成一个体面的模板,正确使用std::atomic_flag。感谢所有!

atomic_flag是一个非常低级的结构,不打算被广泛使用。也就是说,我相信你的用法可以按照你的意愿工作,除了可能在特殊情况下清除旗帜。如果出现std::exception所匹配的异常之外的异常,则不清除该标志。

通常应该使用RAII来处理这类事情。"R"通常代表"资源",但我喜欢乔恩·卡尔布用"责任"来代替。在设置了标志之后,您有责任在完成后清除标志,因此您应该使用RAII来确保执行该责任。如果在特殊情况下需要做的所有事情都可以这样做,那么try/catch对就会消失。

if ( !std::atomic_flag_test_and_set( &::_my_flag ) )
{
    flag_clearer x(&::_my_flag);
    // do my stuff here
}

但是你不需要自己写一个flag_clearer类型。相反,您可以简单地使用更高级的结构,如互斥锁和lock_guard:

namespace
{
    std::mutex my_flag;
}
myclass::do_something()
{
    if ( my_flag.try_lock() )
    {
        std::lock_guard<std::mutex> x(my_flag, std::adopt_lock);
        // do my stuff here
    }
    // else, we're already doing something in another thread, let's exit
}

是的,如果其他线程已经设置了标志并且没有人清除它,那么将跳过if块内的代码。如果没有其他代码与该标志混淆,则表示当前有线程正在执行该块。

原子标志是相当低级的;考虑使用atomic_bool。此外,由于这是c++,您可以使用成员函数进行set和clear操作。

编辑:

不,atomic_bool不容易做你想要的。坚持使用atomic_flag