删除原子/受防护数据中的数据

时间:2015-03-25 11:47:09

标签: c++ c++11 atomic producer-consumer

假设我们在C ++ 11程序中使用标准的消费者/生产者模式:(来自:http://en.cppreference.com/w/cpp/atomic/memory_order

#include <thread>
#include <atomic>
#include <cassert>
#include <string>

std::atomic<std::string*> ptr;
int data;

void producer()
{
    std::string* p = new std::string("Hello");
    ptr.store(p, std::memory_order_release);
}

void consumer()
{
    std::string* p2;
    while (!(p2 = ptr.load(std::memory_order_consume)))
        ;
    assert(*p2 == "Hello"); // never fires: *p2 carries dependency from ptr

    // yea well, it actually uses p2 for quite a while probably....
}

int main()
{
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join(); t2.join();
}

现在,我想稍微改变生产者代码的行为。我不想简单地设置字符串,而是希望覆盖字符串。 E.g:

void producer()
{
    std::string* p = new std::string("Hello");
    ptr.store(p, std::memory_order_release);

    // do some stuff

    std::string* p2 = new std::string("Sorry, should have been Hello World");
    ptr.store(p2, std::memory_order_release);

    // **
}

这里的生产者负责生成字符串,这意味着在我的简单世界中它也应该负责销毁这些字符串。

在标有'**'的行中,我们应该销毁字符串'p',这就是这个问题。

您可能会考虑添加(在标记的行)的解决方案:

delete p;

然而,这会破坏程序,因为消费者可能在我们删除它之后使用该字符串 - 毕竟,消费者使用指针。此外,这意味着生产者等待消费者,这是不必要的 - 我们只是希望清理旧的内存。使用ref计数智能指针似乎是不可能的,因为atomic只支持那么多类型。

解决此问题的最佳(最有效)方法是什么?

1 个答案:

答案 0 :(得分:2)

您可以执行atomic exchange,它将返回原子变量的先前值。

变量ptr则有两种状态:它可以没有数据可用,在这种情况下它等于nullptr,或者有数据供消费。

消费数据,任何消费者都可以与ptr交换nullptr

  • 如果没有数据,仍然没有任何数据,消费者将不得不再次尝试(这有效地构建了一个自旋锁)。

  • 如果有数据,消费者现在就拥有所有权,并在不再需要时负责将其删除。

要生成生成数据,生产者会将ptr与指向生成数据的指针进行交换。

  • 如果没有数据,则前一个指针将等于nullptr并且数据已成功生成。
  • 如果有数据,生产者有效地收回以前生成的数据的所有权。然后它可以删除对象,或者 - 更有效地 - 只需将其重新用于下一次生产。