我需要一个以循环方式使用的线程安全的Buffer-objects池。我通常会在那里放一个互斥量来使增量和模数线程安全,但是可以用std :: atomic写它吗?这是一个示例界面。如果它使事情变得更容易,缓冲区的总数可以是2的幂。永远不会在类之外访问下一个缓冲区索引。
class Buffer;
class BufferManager
{
public:
BufferManager( size_t totalBuffers = 8 ) : mNextBufferIndex( 0 ), mTotalBuffers( totalBuffers )
{
mBuffers = new Buffer*[mTotalBuffers];
}
Buffer* GetNextBuffer()
{
// How to make this operation atomic?
size_t index = mNextBufferIndex;
mNextBufferIndex = ( mNextBufferIndex + 1 ) % mTotalBuffers;
return mBuffers[index];
}
private:
Buffer** mBuffers;
size_t mNextBufferIndex;
size_t mTotalBuffers;
};
答案 0 :(得分:5)
选择
后可以安全地使用模数7
答案 1 :(得分:1)
您可以声明mNextBufferIndex
和std::atomic_ullong
,然后使用
return mBuffers[(mNextBufferIndex++) % mTotalBuffers];
增量将是原子的,你在返回之前计算模数。
使用非常大的无符号将避免在计数器换行时发生的问题。
答案 2 :(得分:0)
据我所知,有硬件辅助互锁操作。一个这样的操作是增量。您不需要使其更复杂,因为模运算可以独立于递增操作。
std::atomic
会使operator++
重载,我认为它具有原子保证。
Buffer* GetNextBuffer()
{
// Once this inc operation has run the return value
// is unique and local to this thread the modulo operation
// does not factor into the consistency model
auto n = mNextBufferIndex++;
auto pool_index = n % mTotalBuffers;
return mBuffers[pool_index];
}
如果您想使用模数或任何其他复杂算法,请使用比较和交换版本。
比较和交换或比较和交换之间的想法是你进行计算,当你想将值写回内存位置(共享或其他)时,只有在没有其他人没有修改值的情况下它才会成功在此期间(如果他们你只是重试操作,忙等待)。这只需要一个可预测的编号方案,这通常很有可能。
Buffer* GetNextBuffer()
{
// Let's assume that we wanted to do this
auto n = (mNextBufferIndex % mTotalBuffers) ++;
mNextBufferIndex = n;
return mBuffers[n];
}
假设mNextBufferIndex
是std::atomic
。
Buffer* GetNextBuffer()
{
// Let's assume that we wanted to do this
auto n = (mNextBufferIndex % mTotalBuffers)++;
// This will now either succeed or not in the presence of concurrency
while (!std::compare_exchange_weak(mNextBufferIndex, n)) {
n = (mNextBufferIndex % mTotalBuffers)++;
}
return mBuffers[n];
}
您可能认为这更像是乐观并发控制,但如果您将自己限制为对原子的定义过于狭窄,那么您将无法完成任何工作。
暂且不论我在这里计算的是完全无意义的,这表明compare_exchange
操作有多强大,以及如何使用它来制作任何算术原子。当您有多个相互依赖的计算时,问题就出现了。在这种情况下,您需要编写大量的恢复例程。
但是,互锁操作本身并不是免费的,并且会驱逐处理器中的缓存行。
作为参考,我可以向Mike Acton推荐关于增量问题的paper slides。
答案 3 :(得分:-1)
当您有两个输入时,这是不可能的。你可以用原子做的就是使用CPU支持的原子指令(我还没有听到可以做增量加模数作为操作的芯片),或者你可以先做计算而不是设置输入所提供的值不要改变 - 但你的功能确实有两个输入,所以这也不起作用。