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

Disadvantages of using reference parameter to return value in lock-free pop()

我目前正在阅读威廉姆斯 "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;
}

引用这段代码的讨论:

The second problem is an exception-safety issue. When we first introduced the thread-safe stack back in chapter 3, you saw how just returning the object by value left you with an exception safety issue: if an exception is thrown when copying the return value, the value is lost. In that case, passing in a reference to the result was an acceptable solution because you could ensure that the stack was left unchanged if an exception was thrown. Unfortunately, here you don’t have that luxury; you can only safely copy the data once you know you’re the only thread returning the node, which means the node has already been removed from the queue. Consequently, passing in the target for the return value by reference is no longer an advantage: you might as well just return by value. If you want to return the value safely, you have to use the other option from chapter 3: return a (smart) pointer to the data value

我不明白引用的使用如何导致异常问题。我在这里看到单词 'copy',但它不能与引用一起使用,'initialization'、'assignment' 但肯定不能与 'copy' 一起使用。所以,我不明白在这个 'copy'.

之后写的所有内容

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

但这并没有为我澄清问题,因为他再次使用 'copy' 和引用:

"Normal" 堆栈:

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

With a "normal" stack, the advantage of a reference parameter for the result is that you can copy the result before you remove the node from the stack, and if the copy to the result throws then the node is still there for another call to take.

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

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

我认为Williams使用"copy"这个词是因为它是这里可以使用的最笼统的世界。他还谈到了返回值,在这种情况下,可能会调用复制构造函数、移动构造函数、赋值运算符和移动赋值运算符,具体取决于代码。他没有用很多动词来膨胀文本,而是使用单词 "copy" 因为唯一重要的部分是我们在一个地方有数据,我们需要在另一个地方。