使用 atomic c++11 实现线程安全递减计数器为零

using atomic c++11 to implement a thread safe down counter to zero

我是原子技术的新手,尝试为以下代码实现安全线程版本:

// say m_cnt is unsigned

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

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

每个调用 dec_counter 的线程都必须将其减一,而 "Do something" 应该只执行一次 - 当计数器减为 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 
        }

}

atomic 的特点是只有一个语句是原子的。

如果你写

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++ 中的小原子,所有简单的数学运算通常都得到有效支持

对于更复杂的类型和表达式,您需要使用 read/modify/write 成员方法。

这些允许您读取当前值,计算新值,然后调用 compare_exchange_strongcompare_exchange_weak 说 "if the value has not changed, then store my new value, otherwise give me the new current value" 单个原子操作。你可以把它放在一个循环中并不断重新计算新值,直到你足够幸运,你的线程是唯一的作者。如果没有太多线程尝试过于频繁地更改值,这也是相当有效的。