0

我正在尝试实现队列的线程安全版本,并在实现 pop() 的包装器时遇到问题。参考下面的代码。由于限制,无法粘贴整个代码。

bool internal_pop_front_no_lock(T& item)
{
    bool isDataAvailable = false;

    if (!m_Queue.empty())
    {
        item = m_Queue.front();
        m_Queue.pop();
        isDataAvailable = true;
    }

   return isDataAvailable;
}

现在我觉得行item = m_Queue.front();会复制数据。有没有办法避免复制?还是我误解了什么?

4

2 回答 2

2

只有一种方法可以避免复制,如果您移动此对象然后返回它。因为即使您可以m_Queue.front()在不复制的情况下访问,一旦您这样做,m_Queue.pop();该对象就会被销毁,如果您需要在此之后访问该对象,则需要复制或移动该对象。所以像这样的事情是你唯一的机会:

std::optional<T> internal_pop_front_no_lock()
{
    std::optional<T> result;

    if (!m_Queue.empty())
    {
        result = std::move(m_Queue.front());
        m_Queue.pop();
    }

   return result;
}
于 2020-11-23T14:01:56.120 回答
1

std::queue存储实际值时,您能做的最好的事情就是将值移出(如 中所示std::move)。对于许多复杂的对象,即使复制很昂贵,移动也很便宜。

如果你有一个复杂的对象,移动成本很高,你可以有一个unique_ptrs 队列;这些移动起来很便宜(而且不可能复制)。

使用 a 或其他东西测试您的线程安全队列std::unique_ptr<int>并确保它有效。然后你就会知道你的队列没有复制操作。然后由您队列的用户(可能是您自己)来确保他们放入其中的物品足够便宜以便移动。

A boost::optional<T>orstd::optional<T>在这里非常有用,因为您有 aT&和 abool来指示它是否已填充。但核心是:

result = std::move(m_Queue.front()); // move

代替

result = m_Queue.front(); // copy

当您提取值时。

...

我自己,我会对这种方法持怀疑态度。它的长名称是一个危险信号,它所做的只是包装了一些std::queue方法。与直接调用方法相比,它看起来并没有降低复杂性std::queue

根据我的经验,线程安全队列的棘手部分是决定通知的工作方式、中止、批处理操作(pop_many、push_many)等。相比之下,与 的底层交互queue非常简单。

于 2020-11-23T16:14:34.993 回答