以下是Anthony Williams'中线程安全队列的代码。我在.h
文件中收集的 C ++并发操作。
#ifndef THREADSAFE_QUEUE_H
#define THREADSAFE_QUEUE_H
// Anthony Williams' fine-grained lock-based thread-safe queue.
#include <mutex> // for std::mutex
#include <condition_variable> // for std::condition_variable
#include <memory> // for std::shaerd_ptr and std::unique_ptr
#include <utility> // for std::move
template <typename T>
class threadsafe_queue
{
private:
struct node
{
std::shared_ptr<T> data;
std::unique_ptr<node> next;
};
std::mutex head_mutex;
std::unique_ptr<node> head;
std::mutex tail_mutex;
node* tail;
std::condition_variable data_cond;
public:
threadsafe_queue():
head(new node), tail(head.get())
{}
threadsafe_queue(const threadsafe_queue& other)=delete;
threadsafe_queue& operator=(const threadsafe_queue& other)=delete;
std::shared_ptr<T> try_pop();
bool try_pop(T& value);
std::shared_ptr<T> wait_and_pop();
void wait_and_pop(T& value);
void push(T new_value);
void empty();
private:
node* get_tail()
{
std::lock_guard<std::mutex> tail_lock(tail_mutex);
return tail;
}
std::unique_ptr<node> pop_head()
{
std::unique_ptr<node> old_head = std::move(head);
head = std::move(old_head->next);
return old_head;
}
std::unique_lock<std::mutex> wait_for_data()
{
std::unique_lock<std::mutex> head_lock(head_mutex);
data_cond.wait(head_lock, [&]{return head.get()!=get_tail();});
return std::move(head_lock);
}
std::unique_ptr<node> wait_pop_head()
{
std::unique_lock<std::mutex> head_lock(wait_for_data());
return pop_head();
}
std::unique_ptr<node> wait_pop_head(T& value)
{
std::unique_lock<std::mutex> head_lock(wait_for_data());
value=std::move(*head->data);
return pop_head();
}
std::unique_ptr<node> try_pop_head()
{
std::unique_lock<std::mutex> head_lock(head_mutex);
if(head.get()==get_tail())
{
return std::unique_ptr<node>();
}
return pop_head();
}
std::unique_ptr<node> try_pop_head(T& value)
{
std::unique_lock<std::mutex> head_lock(head_mutex);
if(head.get()==get_tail())
{
return std::unique_ptr<node>();
}
value=std::move(*head->data);
return pop_head();
}
};
/*
* PUBLIC INTERFACE
*/
// try pop.
template <typename T>
std::shared_ptr<T> threadsafe_queue<T>::try_pop()
{
std::unique_ptr<node> const old_head=try_pop_head();
return old_head?old_head->data:std::shared_ptr<T>();
}
template <typename T>
bool threadsafe_queue<T>::try_pop(T& value)
{
std::unique_ptr<node> const old_head=try_pop_head(value);
return old_head;
}
// wait and pop.
template <typename T>
std::shared_ptr<T> threadsafe_queue<T>::wait_and_pop()
{
std::unique_ptr<node> const old_head=wait_pop_head();
return old_head->data;
}
template <typename T>
void threadsafe_queue<T>::wait_and_pop(T& value)
{
std::unique_ptr<node> const old_head=wait_pop_head(value);
}
// push.
template <typename T>
void threadsafe_queue<T>::push(T new_value)
{
std::shared_ptr<T> new_data(
std::make_shared<T>(std::move(new_value)));
std::unique_ptr<node> p(new node);
{
std::lock_guard<std::mutex> tail_lock(tail_mutex);
tail->data=new_data;
node* const new_tail=p.get();
tail->next=std::move(p);
tail=new_tail;
}
data_cond.notify_one();
}
// empty.
template <typename T>
void threadsafe_queue<T>::empty()
{
std::lock_guard<std::mutex> head_lock(head_mutex);
return (head.get()==get_tail());
}
#endif
代码中有一件事我无法推理,它出现在两点上。在wait_pop_head(T& value)
和try_pop_head(T& value)
中,有value=std::move(*head->data);
。基本上,为了将shared_ptr
的解除引用结果分配给引用,它会将其传递给std::move
。如果你让我知道为什么要这样做,我感激不尽?为什么不应该使用value=*head->data;
代替?
评论中提出的另一个问题是,为什么要std::shared_ptr
代替std::unique_ptr
?
答案 0 :(得分:1)
如果类型T
定义了移动赋值运算符,那么提供通过调用std::move()
获得的右值引用将允许使用更高效的运算符,而不是强制使用复制赋值运算符。
如果类型T
没有定义移动赋值运算符,则可以向其赋值运算符提供T&&
,这可能需要{{1}类型的参数1}}或者只是const T&
。在任何一种情况下,类型T
的参数都可以转换为参数类型。
至于使用T&&
而不是std::shared_ptr
,我不明白为什么它也是必要的。看起来可以从std::unique_ptr
和std::unique_ptr
返回try_pop()
,将指针移出即将被销毁的wait_and_pop()
实例。我能想出的唯一一个论点是允许来自&#34; pop&#34;功能结束了可共享的参考,认为这是一个更灵活的选择。我粗略地阅读实现并没有表明任何情况下两个threadsafe_queue::node
实例会指向相同的&#34;数据&#34;价值,所以我无法找到该设计选择的任何内部原因。
答案 1 :(得分:0)
对于第一个,如果T已经分配了动态内存,那么move
可以减少很多资源。在这种情况下,函数名为 *_pop_head
,这意味着调用后头节点的 data
应该被视为无用。
所以使用move
将T的动态内存的所有权转移给目标T
对象是合理的。
对于第二个,我能想到的唯一原因是,如果我们想要一个top
成员函数,我们可以将头节点的data
放在多个地方。>