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

Deleting data in atomic / fenced data

假设我们在 C++11 程序中使用标准 consumer/producer 模式:(来自: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();
}

现在,我想稍微更改一下生产者代码的行为。我不想简单地设置一个字符串,而是 overwrite 一个字符串。例如:

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;

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

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

您可以执行 atomic exchange,这将 return 原子变量的先前值。

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

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

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

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

为了生产数据,生产者交换ptr与指向生产数据的指针。

  • 如果没有数据,前一个指针会等于nullptr,数据成功产生。
  • 如果有数据,生产者实际上收回了之前生产的数据的所有权。然后它可以删除该对象,或者 - 更有效地 - 简单地在下一次生产中重新使用它。