использование atomi c c ++ 11 для реализации поточно-безопасного счетчика до нуля - PullRequest
0 голосов
/ 05 февраля 2020

Я новичок в технике Atomi c и пытаюсь реализовать безопасную версию потока для следующего кода:

// say m_cnt is unsigned

void Counter::dec_counter()
{
    if(0==m_cnt)
       return;

    --m_cnt;
    if(0 == m_cnt)
    {
        // Do seomthing
    }
}

Каждый поток, вызывающий dec_counter, должен уменьшить его на единицу и "Сделать что-то" должно быть сделано только один раз - когда счетчик уменьшен до 0. После борьбы с ним, я сделал следующий код, который делает это хорошо (я думаю), но мне интересно, если это способ сделать это, или есть лучший способ. Спасибо.

// m_cnt is std::atomic<unsigned> 

void Counter::dec_counter()
{
        // loop until decrement done
        unsigned uiExpectedValue;
        unsigned uiNewValue;
        do
        {
            uiExpectedValue = m_cnt.load();

            // if other thread already decremented it to 0, then do nothing.
            if (0 == uiExpectedValue)
                return;

            uiNewValue = uiExpectedValue - 1;

            // at the short time from doing 
            //     uiExpectedValue = m_cnt.load(); 
            // it is possible that another thread had decremented m_cnt, and it won't be equal here to uiExpectedValue,
            // thus the loop, to be sure we do a decrement
        } while (!m_cnt.compare_exchange_weak(uiExpectedValue, uiNewValue));

        // if we are here, that means we did decrement . so if it was to 0, then do something
        if (0 == uiNewValue)
        {
            // do something 
        }

}

1 Ответ

1 голос
/ 07 февраля 2020

С атомом c дело в том, что только один оператор является атомом c.

Если вы напишите

std::atomic<int> i {20}
...
if (!--i)
    ...

Тогда только 1 поток введет оператор if.

Однако, если вы разделите изменение и тест, тогда другие потоки могут попасть в разрыв, и вы можете получить странные результаты:

std::atomic<int> i {20}
...
--i;
    // other thread(s) can modify i just here
if (!i)
    ...

Конечно, вы можете разделить условие проверить декремент с помощью локальной переменной:

std::atomic<int> i {20}
...
int j=--i;
    // other thread(s) can modify i just here
if (!j)
    ...

Все простые математические операции, как правило, эффективно поддерживаются для малых атомов в c ++

Для более сложных типов и выражений вам необходимо использовать методы-члены чтения / изменения / записи.

Они позволяют вам прочитать текущее значение, вычислить новое значение и затем вызвать compare_exchange_strong или compare_exchange_weak сказать "если значение не изменилось, затем сохранить мое новое значение, в противном случае дайте мне новое текущее значение "a atomi c операция. Вы можете вставить это в al oop и продолжать пересчитывать новое значение, пока вам не повезет, что ваш поток является единственным автором. Если не слишком много потоков, которые слишком часто пытаются изменить значение, это также достаточно эффективно.

...