Например, вы можете безопасно увеличивать и уменьшать std :: atomic_int. Но если вам нужно проверить на переполнение или выполнить какую-то подпрограмму условно на основе значения, тогда блокировка все равно нужна. Поскольку вы должны сравнить значение, и поток может быть заменен сразу после успешного сравнения, другой поток изменяет … ошибка.
Но если вам нужна блокировка, вы можете просто использовать целое число вместо атомарного. Я прав?
Нет, вы все равно можете использовать std :: atomic даже условно.
Во-первых, если вы используете std::atomic<unsigned int>
тогда поведение переполнения хорошо определено (хотя, возможно, не то, что вы хотите). Если вы используете целочисленное переполнение со знаком, оно не очень хорошо определено, но до тех пор, пока вы его не нажмете, это не имеет значения.
Если вам абсолютно необходимо проверить переполнение или иным образом действовать условно, вы можете использовать сравнить-обмен. Это позволяет вам прочитать значение, решить, хотите ли вы поработать с ним, а затем атомарно обновить значение обратно, если оно не изменилось. И ключевая часть здесь — система сообщит вам, если атомное обновление не удалось, в этом случае вы можете вернуться к началу, прочитать новое значение и принять решение снова.
В качестве примера, если бы мы только хотели установить максимальное значение атомного целого числа равным 4 (например, при некотором пересчете), мы могли бы сделать:
#include <atomic>
static std::atomic<int> refcount = 0;
int val = refcount; // this doesn't need to be in the loop as on failure compare-exchange-strong updates it
while(true)
{
if(val == 4)
{
// there's already 4 refs here, maybe come back later?
break;
}
int toChangeTo = val + 1;
if(refcount.compare_exchange_strong(val, toChangeTo))
{
// we successfully took a ref!
break;
}
// if we fail here, another thread updated the value whilst we were running, just loop back and try again
}
В приведенном выше коде вы можете использовать compare_exchange_weak
вместо. Иногда это может происходить внезапно, поэтому вам нужно сделать это в цикле. Тем не менее, у нас есть цикл в любом случае (и в целом вы всегда будете делать так, как вам нужно, чтобы справиться с реальными сбоями) и так compare_exchange_weak
имеет много смысла здесь.
Других решений пока нет …