[英]Lockless circular buffer with single producer singular consumer
我有一个使用者线程,它永远不能锁定或分配内存,一个生产者线程可以。 我想实现一个两处循环缓冲区,以便能够从生产者向消费者线程提供数据,并且有一个界限,即每当没有新数据可供使用时,消费者就将重用已经可用的数据。
这是我现在想出的:
bool newDataAvailable = false;
bool bufferEmpty = true;
foo* currentData = new foo();
foo* newData = new foo();
void consumer() {
while(true) {
currentData->doSomething();
if(newDataAvailable) {
foo* tmp = currentData;
// Objects are swapped so the old one can be reused without additional allocations
currentData = newData;
newData = tmp;
newDataAvailable = false;
bufferEmpty = true;
}
}
}
void producer() {
while(true) {
while(!bufferEmpty) { wait(); }
newData->init();
bufferEmpty = false;
newDataAvailable = true;
}
}
这个天真的实现可以吗? 我知道对变量的读写可能是非原子的,因此我应该使用原子存储,但这会导致锁定。 这里需要使用原子存储吗? 另外,我想消除生产者中的主动等待,我认为我可以使用std::condition_variable
,但是它们需要使用互斥体,因此我负担不起。
编写不使用互斥量共享变量的多线程代码非常困难。 请参见无锁编程简介 ,无锁缓冲区 。
如果您绝对必须避免使用互斥锁,那么我强烈建议您使用预制的无锁队列,例如Boost.lockfree或MPMCQueue作为轻量的非增强型替代方法。
我知道对变量的读写可能是非原子的,因此我应该使用原子存储,但这会导致锁定。
std::atomic
通常对所有原始类型(不超过您的CPU的本机大小)都是无锁的(不使用互斥锁)。 您可以通过调用std::atomic<T>::is_lock_free
来检查std::atomic
是否将互斥体用于给定类型
这里需要使用原子存储吗?
是的,一点没错。 您需要使用互斥或原子。
另外,我想消除生产者中的活动等待,我认为我可以使用std :: condition_variable
当您不能使用互斥锁时,唯一的选择是使用自旋锁。 如果在您的上下文中允许,则可以在自旋锁中使用std::this_thread::yield()
来减少CPU负载。 (但是互斥锁可能会更快)
编辑:仅2个原子的潜在解决方案是:
std::atomic<foo*> currentData = new foo();
std::atomic<foo*> newData = new foo();
void consumer() {
foo* activeData = currentData;
while (true) {
activeData->doSomething();
foo* newItem = currentData;
if (newItem != activeData) {
newData = activeData;
activeData = newItem;
}
}
}
void producer() {
while (true) {
foo* reusedData = newData;
if (!reusedData)
continue;
newData = nullptr;
reusedData->init();
currentData = reusedData;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.