在无锁的pop()中使用引用参数返回值的缺点



我目前正在阅读Williams的《c++ Concurrency in Action》。现在我停在专门讨论无锁pop()实现的主题上。

锁定流行:

void pop(T& result)
{
 node* old_head = head.load();
 while(!head.compare_exchange_weak(old_head,old_head->next));
 result=old_head->data;
}

下面是对这段代码讨论的引用:

第二个问题是异常安全问题。当我们在第3章第一次介绍线程安全堆栈时,您看到了按值返回对象是如何给您带来异常安全问题的:如果在复制返回值时抛出异常,则该值将丢失。在这种情况下,传递对结果的引用是一种可接受的解决方案,因为您可以确保在抛出异常时堆栈保持不变。不幸的是,在这里你没有这种奢侈;只有当您知道您是唯一返回节点的线程时,您才能安全地复制数据,这意味着节点已经从队列中删除。因此,通过引用将返回值的目标传递给不再是一个优势:您还不如直接通过值返回。如果你想安全地返回值,你必须使用第三章中的另一个选项:返回一个指向数据值 的(智能)指针

我不明白为什么使用引用会导致异常问题。我在这里看到了"复制"这个词,但它不能用于引用,"初始化","赋值",但肯定不能用于"复制"。所以,我不明白写在这个'copy'之后的所有内容。

在谷歌上,我找到了Williams的解释:https://forums.manning.com/posts/list/30887.page

,但这并没有为我澄清问题,因为他又用了"copy"和引用:

"正常"堆栈:

void pop(T& value)
{
 std::lock_guard<std::mutex> lock(m);
 if(data.empty()) throw empty_stack();
 value=data.top();
 data.pop();
}

对于"正常"堆栈,引用形参对结果的优点是,在从堆栈中删除节点之前,可以复制结果,如果对结果的复制抛出,则该节点仍然存在,可供下一次调用。

我的问题是:在这种情况下使用引用是否会导致异常的产生,为什么我可以直接按值返回?

当您执行result=old_head->data;时,您将old_head->data复制到result。您是对的,在这种情况下使用赋值操作符而不是复制构造函数。但这并不重要,重要的是,一些非琐碎的操作可能需要将数据从old_head->data复制到result,这些操作可能会触发异常。

我认为威廉姆斯使用"复制"这个词是因为它是这里可以使用的最一般的世界。他还谈到了返回值在这种情况下,可能会调用复制构造函数,移动构造函数,赋值操作符和移动赋值操作符,这取决于代码。而不是用大量的动词膨胀文本,他只是使用单词"复制",因为唯一重要的部分是我们在一个地方有数据,我们需要它在另一个地方。

相关内容

  • 没有找到相关文章

最新更新