假设我们在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只支持那么多类型。
解决此问题的最佳(最有效)方法是什么?
答案 0 :(得分:2)
您可以执行atomic exchange,它将返回原子变量的先前值。
变量ptr
则有两种状态:它可以没有数据可用,在这种情况下它等于nullptr
,或者有数据供消费。
要消费数据,任何消费者都可以与ptr
交换nullptr
。
如果没有数据,仍然没有任何数据,消费者将不得不再次尝试(这有效地构建了一个自旋锁)。
如果有数据,消费者现在就拥有所有权,并在不再需要时负责将其删除。
要生成生成数据,生产者会将ptr
与指向生成数据的指针进行交换。
nullptr
并且数据已成功生成。