c++ - 并发安全栈接口(interface)方法 : correct or not?

标签 c++ multithreading

我遇到了 stack<>::pop() 的接口(interface)方法的线程安全堆栈实现:

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

当然,我的问题与并发无关,但是为什么将堆栈顶部的值移动到变量值中呢?我的理解是一旦它被移动,你就不能弹出它,因为它不再存在。

要么是我找到它的来源中的错误,要么如果有人可以向我解释,我将不胜感激。

谢谢,

最佳答案

value=std::move(data.top()); <----- why not just value = data.top()?



这在很大程度上取决于 T是,基本上它会尝试使用移动构造函数,T(T &&mv)而不是复制构造函数T(const T &cp)如果移动构造函数存在。

在这两种情况下~T()将为 data.pop(); 上的原始对象调用线。

首先,使用移动构造函数可能是必填 .有些对象是可移动的,但不可复制,例如一个 unique_ptr .

其次,在提供移动的地方,它通常更有效。例如说Tstd::vector ,复制构造函数将分配另一个数组,然后复制每个元素(这也可能很昂贵),然后无论如何都会删除原始数组。这是相当浪费的。

移动构造函数只是通过将内部数据数组从一个 vector 移动到一个新 vector 来保留原始元素,并将原始元素(在这种情况下无论如何都将被删除)处于未指定但有效的状态(可能是“空”) .

它可能看起来像:
template<typename T> class vector
{
public:
    vector<T>(vector<T> &&mv)
        : arr(mv.arr) , arr_len(mv.arr_len), arr_capacity(mv.arr_capacity)
    {
        mv.arr = nullptr;
        mv.arr_len = 0;
        mv.arr_capacity = 0;
    }
    ...
private:
    T *arr;
    size_t arr_len;
    size_t arr_capacity;
};

由于原始对象状态在一般情况下是“未指定”的,因此如果您想继续使用原始对象,则必须小心。像在 pop 中那样销毁它案例还可以,分配也可以。
T tmp = std::move(some_value);
some_value.foo(); // In general, what state some_value is in is unknown, this might vary even from compiler to compiler
some_value = some_other_value; // But assignment should work
some_value.foo(); // So it is now in a known state

例如,它可以在不复制任何“内容”的情况下实现“交换”。
template<typename T>  void swap(T &a, T &b)
{
    T tmp = std::move(a);
    a = std::move(b);
    b = std::move(tmp);
}

许多类型将更加指定,例如 std::vector , 它 promise 是 empty() .
std::vector<T> tmp = std::move(some_array);
assert(some_array.empty()); // guaranteed
some_array.push_back(x); // guaranteed to have one element

you can't pop it as it is no longer there.



所以这里重要的一点是data.top()不删除元素,所以它仍然存在。并且移动实际上并没有删除这个东西,只是让它处于某种未指定的状态。

Concurrency-safe stack interface method



在并发的单独主题上,这里的东西是 top访问该值和 pop删除它在同一个锁下。为了安全起见,对该实例堆栈的所有访问都必须使用同一个锁实例,因此请确保任何 data.push还持有锁。

关于c++ - 并发安全栈接口(interface)方法 : correct or not?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59354884/

相关文章:

java - spring boot 应用程序的 tomcat 中的默认线程池

c++ - 从工作线程输出到 cin (c++)

c# - 多个用户写入同一个文件

c# - 通过消息分片ID实现多线程消费者

c++ - 关闭部分代码的优化

c++ - std::thread 构造函数?特化?。 g++ 与 clang++

c++ - 你如何声明一个 const 函数指针数组?

c++ - boost/输出到文件

c++ - std::vector 向下调整大小

python - 使用 python 和 tkinter 实时绘制串行数据