У меня есть простое логическое значение, которое мне нужно протестировать и установить потокобезопасным способом. Если один поток уже работает, я хочу, чтобы второй поток вышел. Если я понимаю 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
набери себя. Вместо этого вы можете просто использовать конструкции более высокого уровня, такие как mutex и 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 ++, вы можете использовать функции-члены для набора и очистки.
РЕДАКТИРОВАТЬ:
Нету, atomic_bool
не легко делать то, что вы хотите. Придерживаться atomic_flag
…