C++ 11 std::atomic flag, я правильно использую это?

у меня есть простое логическое значение, которое мне нужно проверить и установить потокобезопасным способом. Если один поток уже работает, я хочу, чтобы второй поток вышел. Если я правильно понял ... --5-->std::atomic_flag правильно, это должно работать нормально. Однако я не уверен, что понимаю std::atomic_flag правильно :) я не могу найти много простых примеров в интернете, за исключением этого примера spinlock:

// 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

Update: обновленный код на основе предложений ниже, образуя достойный шаблон для надлежащего использования из std::atomic_flag. Спасибо всем!

2 ответов


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++, вы можете использовать функции-члены для набора и понятно.

EDIT:

Неа, atomic_bool нелегко делать то, что вы хотеть. Палка с atomic_flag...