使用 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_strong
或 compare_exchange_weak
说 "if the value has not changed, then store my new value, otherwise give me the new current value" 单个原子操作。你可以把它放在一个循环中并不断重新计算新值,直到你足够幸运,你的线程是唯一的作者。如果没有太多线程尝试过于频繁地更改值,这也是相当有效的。
我是原子技术的新手,尝试为以下代码实现安全线程版本:
// 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_strong
或 compare_exchange_weak
说 "if the value has not changed, then store my new value, otherwise give me the new current value" 单个原子操作。你可以把它放在一个循环中并不断重新计算新值,直到你足够幸运,你的线程是唯一的作者。如果没有太多线程尝试过于频繁地更改值,这也是相当有效的。