简体   繁体   English

具有单个生产者单个使用者的无锁循环缓冲器

[英]Lockless circular buffer with single producer singular consumer

I have a consumer thread that must never lock nor allocate memory, and a producer thread that can. 我有一个使用者线程,它永远不能锁定或分配内存,一个生产者线程可以。 I want to implement a two place circular buffer to be able to provide data to the consumer thread from the producer, with the bound that whenever no new data is available to consume, the consumer just re-uses the already available data. 我想实现一个两处循环缓冲区,以便能够从生产者向消费者线程提供数据,并且有一个界限,即每当没有新数据可供使用时,消费者就将重用已经可用的数据。

This is what I've come up with for now: 这是我现在想出的:

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;
  }
}

Is this naive implementation ok? 这个天真的实现可以吗? I know reading and writing to variables can be non-atomic, so I should use an atomic storage, but those can cause locks. 我知道对变量的读写可能是非原子的,因此我应该使用原子存储,但这会导致锁定。 Is the use of atomic storage needed here? 这里需要使用原子存储吗? Also, I'd like to eliminate the active wait in the producer, and I thought I could use a std::condition_variable , but they require the use of mutexes and I cannot afford them. 另外,我想消除生产者中的主动等待,我认为我可以使用std::condition_variable ,但是它们需要使用互斥体,因此我负担不起。

Writing multi-threaded code that shares variables without using a mutex is very difficult to get right. 编写不使用互斥量共享变量的多线程代码非常困难。 see An Introduction to Lock-Free Programming , Lock Free Buffer . 请参见无锁编程简介 ,无锁缓冲区

If you absolutely must avoid using mutexes, then i would highly recommend using a pre-made lock-free queue, like eg Boost.lockfree or MPMCQueue as a light non-boost alternative. 如果您绝对必须避免使用互斥锁,那么我强烈建议您使用预制的无锁队列,例如Boost.lockfreeMPMCQueue作为轻量的非增强型替代方法。

I know reading and writing to variables can be non-atomic, so I should use an atomic storage, but those can cause locks. 我知道对变量的读写可能是非原子的,因此我应该使用原子存储,但这会导致锁定。

std::atomic is generally lock-free (doesn't use a mutex) for all primitive types (up to the native size of your cpu). std::atomic通常对所有原始类型(不超过您的CPU的本机大小)都是无锁的(不使用互斥锁)。 You can check if std::atomic will use a mutex for a given type by calling std::atomic<T>::is_lock_free 您可以通过调用std::atomic<T>::is_lock_free来检查std::atomic是否将互斥体用于给定类型

Is the use of atomic storage needed here? 这里需要使用原子存储吗?

Yes, absolutely. 是的,一点没错。 You either need to use mutexes or atomics. 您需要使用互斥或​​原子。

Also, I'd like to eliminate the active wait in the producer, and I thought I could use a std::condition_variable 另外,我想消除生产者中的活动等待,我认为我可以使用std :: condition_variable

When you can't use mutexes your only option is to use a spin lock. 当您不能使用互斥锁时,唯一的选择是使用自旋锁。 If it is allowed in your context, you could use std::this_thread::yield() in the spin lock to reduce CPU load. 如果在您的上下文中允许,则可以在自旋锁中使用std::this_thread::yield()来减少CPU负载。 (however a mutex might be faster then) (但是互斥锁可能会更快)

Edit: A potential solution with only 2 atomics would be: 编辑:仅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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM