线程安全队列实现:pop()的高效实现
Thread safe queue implementation : Efficient implementation of pop()
我正在尝试实现队列的线程安全版本,但在围绕 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();
行会复制数据。有什么办法可以避免复制?还是我误会了什么?
只有一种方法可以避免复制,即移动此对象然后 return 它。因为即使你可以在不复制的情况下访问 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;
}
由于 std::queue
存储实际值,您能做的最好的事情就是将值移出(如 std::move
中)。对于许多复杂的对象,即使复制很昂贵,移动也很便宜。
如果你有一个复杂的对象,移动起来很昂贵,你可以有一个 unique_ptr
的队列;这些移动起来很便宜(而且不可能复制)。
使用 std::unique_ptr<int>
或其他方法测试您的线程安全队列并确保其正常工作。然后你就会知道你的队列没有复制操作。然后由您队列中的用户(可能是您自己)来确保他们放入其中的物品足够便宜,可以移动。
A boost::optional<T>
或 std::optional<T>
在这里非常有用,因为您有一个 T&
和一个 bool
来指示它是否已填充。但核心是:
result = std::move(m_Queue.front()); // move
而不是
result = m_Queue.front(); // copy
提取值时。
...
我自己对这种方法持怀疑态度。它的长名称是一个危险信号,它所做的只是包装一些 std::queue
方法。与直接调用 std::queue
方法相比,它看起来并没有降低复杂性。
根据我的经验,线程安全队列的棘手部分是决定通知如何工作、中止、批处理操作(pop_many、push_many)等。 queue
相比之下真的很简单。
我正在尝试实现队列的线程安全版本,但在围绕 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();
行会复制数据。有什么办法可以避免复制?还是我误会了什么?
只有一种方法可以避免复制,即移动此对象然后 return 它。因为即使你可以在不复制的情况下访问 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;
}
由于 std::queue
存储实际值,您能做的最好的事情就是将值移出(如 std::move
中)。对于许多复杂的对象,即使复制很昂贵,移动也很便宜。
如果你有一个复杂的对象,移动起来很昂贵,你可以有一个 unique_ptr
的队列;这些移动起来很便宜(而且不可能复制)。
使用 std::unique_ptr<int>
或其他方法测试您的线程安全队列并确保其正常工作。然后你就会知道你的队列没有复制操作。然后由您队列中的用户(可能是您自己)来确保他们放入其中的物品足够便宜,可以移动。
A boost::optional<T>
或 std::optional<T>
在这里非常有用,因为您有一个 T&
和一个 bool
来指示它是否已填充。但核心是:
result = std::move(m_Queue.front()); // move
而不是
result = m_Queue.front(); // copy
提取值时。
...
我自己对这种方法持怀疑态度。它的长名称是一个危险信号,它所做的只是包装一些 std::queue
方法。与直接调用 std::queue
方法相比,它看起来并没有降低复杂性。
根据我的经验,线程安全队列的棘手部分是决定通知如何工作、中止、批处理操作(pop_many、push_many)等。 queue
相比之下真的很简单。