Как сделать сравнение и увеличить атомарно?

В моей попытке разработать поточно-безопасный класс шаблонов слабых указателей С++ мне нужно проверить флаг, указывающий на то, что объект все еще жив, если да, то увеличивайте счетчик ссылок на объекты, и мне нужно выполнить оба действия атомарно.

Я знаю существование функций intrinsics, предоставляемых компилятором, например _InterlockedCompareExchange() и _InterlockedIncrement(). Но то, что я хочу, является функцией interlockedCompareIncrement(), существует ли эффективный способ имитации этого внутреннего использования с использованием других примитивов, по крайней мере на платформе Windows x86?

Ответ 1

Предположим, что value - ваша переменная флага. Он должен быть объявлен volatile.

long curvalue;
long newvalue;

do
{
    curvalue = value;
    newvalue = curvalue + 1;
}
while( _InterlockedCompareExchange( &value, newvalue, curvalue ) != curvalue );

Как вы видите, вы можете обобщить это на любую арифметику, которая вам нужна, изменив операции, которые применяются для вычисления newvalue.

Если вы хотите сравнить два значения одновременно, лучше всего упаковать оба значения в одну переменную, а затем работать с этой единственной переменной. Поскольку вы используете флаг в сочетании с подсчетом ссылок, я бы рекомендовал использовать младший бит value как флаг "живой", а затем увеличивать/уменьшать на 2 за раз. Это позволяет кодировать как флаг, так и счетчик ссылок в одну 32-битную переменную.

Ответ 2

Если вы хотите, чтобы ваша библиотека работала на нескольких процессорах или на нескольких основных машинах, вам необходимо использовать аппаратную поддержку, предоставляемую процессором. Вот несколько ссылок для вас:

http://en.wikipedia.org/wiki/Test-and-set http://software.intel.com/en-us/forums/showthread.php?t=47498

Или вам нужно использовать механизм блокировки, предоставляемый ОС. Например,

http://msdn.microsoft.com/en-us/library/ms684841%28VS.85%29.aspx или http://en.wikipedia.org/wiki/POSIX_Threads