8

我有一个简单的布尔值,需要以线程安全的方式进行测试和设置。如果一个线程已经在工作,我希望第二个线程退出。如果我理解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. 谢谢大家!

4

2 回答 2

8

atomic_flag是一个非常低级的结构,并不打算广泛使用。就是说,我相信您的使用方式可以按您的意愿使用,但在特殊情况下可能会清除标志。如果发生匹配的异常以外的异常,std::exception则标志不会被清除。

通常 RAII 应该用于这类事情。“R”通常代表“资源”,但我喜欢 Jon Kalb 对“责任”的使用。设置标志后,您有责任在完成后清除标志,因此您应该使用 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
}
于 2013-04-04T20:05:43.023 回答
1

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

不过,原子标志的级别相当低;考虑atomic_bool改用。此外,由于这是 C++,因此您可以将成员函数用于 set 和 clear。

编辑:

不,atomic_bool不会轻易做你想做的事。坚持atomic_flag...

于 2013-04-04T19:59:30.047 回答