Без блокировки с помощью InterlockedCompareExchange - PullRequest
5 голосов
/ 03 мая 2011

Я пытаюсь сделать следующий фрагмент кода без блокировки, используя блокированные операции. Есть идеи, как это перевести?

if (m_Ref == 0xFFFF)
    m_Ref = 1;
else
{
    if (++m_Ref == 1)
        CallSomething(); //

}

Я думал что-то вроде

if (InterlockedCompareExchange(&m_Ref, 1, 0xFFFF) != 0xFFFF))
{
    if (InterlockedIncrement(&m_Ref) == 1)
         CallSomething();
}

Есть ли какие-то проблемы / расы в этом?

Ответы [ 2 ]

8 голосов
/ 03 мая 2011

Это выглядит правильно на первый взгляд, но каждый раз, когда вы используете две взаимосвязанные операции подряд, вы подвергаете себя проблеме ABA . В этом случае один поток не может изменить его с 0xFFFF на 1 (ICX возвращает !=0xFFFF), поэтому он идет вперед и принимает ветвь if и увеличивает ее. Перед выполнением InterlockedIncrement другие потоки изменяют m_ref обратно на 0xFFFF, а исходный поток увеличивает 0xFFFF. В зависимости от типа / семантики m_ref эффект будет осторожным, но, несомненно, будет плохим.

Вы должны выполнить одну операцию ICX для 0xFFF до 1 и от X до X + 1 и всегда повторять попытку, если вы потеряли ICX:

volatile <type> m_ref;

<type> ref, newRef, icxref;
do
{
   ref = m_ref;
   newRef = (0xFFFF == ref) ? 1 : ++ref;
   icxref = InterlockedCompareExchange (&m_ref, newRef, ref);
} while (icxref != ref);
if (newRef == 1 && ref != 0xFFFF)
{
   DoSomething ();
}
4 голосов
/ 03 мая 2011

Да, есть гонка. Другой контекст может многое сделать между InterlockedCompareExchange и InterlockedIncrement

...