如何将包含互斥对象的对象从一个函数转移到另一个函数


#include <mutex>
class ConcurrentQueue {
// This class contains a queue and contains functions push to the queue and pop from the queue done in a thread safe manner.
std::mutex m;
};
class Producer {
// This class contains several methods which take some ConcurrentQueue objects and then schedule tasks onto it.
public:
void func(ConcurrentQueue a) {}
};
class Consumer {
// This class contains several methods which take the same ConcurrentQueue objects and then remove the tasks and complete them one by one.
public:
void func(ConcurrentQueue a) {}
};
int main() {
// Here I want to generate the necessary ConcurrentQueue objects and then start threads for producer and consumer methods where I supply it with the required queue objects.
ConcurrentQueue a;
Producer b;
// :( Unfortunately I cannot pass in any of my ConcurrentQueue objects to the methods as apparantly I cannot copy transfer a mutex.
b.func(a); // This line gives compiler error saying the copy constructor is deleted.

return 0;
}

上面的代码通过注释解释了整个情况。我如何更好地设计它,以便能够实现这一点?

如果您不希望ConcurrentQueue类是可复制的,那么不要按值传递;而是使用传递引用(即类型为const ConcurrentQueue &ConcurrentQueue &的参数(。

OTOH如果您确实希望您的ConcurrentQueue类是可复制的(并且您应该仔细考虑允许复制ConcurrentQueue对象是否对您的目标有帮助或有害(,您可以简单地向ConcurrentQueue类别添加一个复制构造函数,该构造函数复制其他成员变量,但不尝试复制std::mute:

class ConcurrentQueue {
// This class contains a queue and contains functions push to the queue and pop from the queue done in a thread safe manner.
std::mutex m;
std::queue<int> q;
public:
ConcurrentQueue()
{
// default constructor
}
ConcurrentQueue(const ConcurrentQueue & rhs)
{
// serialize access to rhs.q so we can read its 
// contents safely while copying them into this->q
const std::lock_guard<std::mutex> lock(const_cast<std::mutex &>(rhs.m));
q = rhs.q;
}
};

请注意,我还需要添加一个默认构造函数,因为一旦您向类中添加任何类型的构造函数,编译器将不再自动为您创建默认构造函数。如果您使用的是C++11或更高版本,则可以改为通过ConcurrentQueue() = default;声明默认构造函数。

尽管Jeremy Friesner提供了一些很好的信息,但我认为值得添加一些更直接的建议。

在这种情况下,几乎可以肯定不想复制任何队列。如果复制一个队列,那么生产者将数据放入一个队列中,消费者则试图从一个完全独立的队列中获取数据。对于你似乎正在努力完成的事情,这不会完成任何有用的事情。

对于生产者/消费者的情况,您几乎总是想要一个队列,因此生产者可以将数据推入队列,消费者将从同一队列中检索数据。

这就给如何做事留下了三个选择:

  • 生产者拥有队列,消费者获得对队列的引用
  • 消费者拥有队列,生产者获得对该队列的引用
  • 两者的父拥有队列,将引用传递给生产者和消费者

[注意:在这种情况下,"引用"实际上意味着"指针"。]

至少一开始,我通常会建议最后一个。这使得终身管理变得相当容易。特别是如果生产者拥有队列,最初的诱惑是生产者在完成其工作后被销毁,但您需要确保队列在消费者消耗完所有数据之前不会被销毁,因此您需要一个从消费者向后到生产者的侧通道,以指示何时可以销毁队列。

当父级创建队列时,它所要做的就是定义队列的实例,并将其传递给生产者和消费者。然后,在它们完成后,它会返回(或其他什么(,队列或多或少会自动销毁,但直到两个孩子都完成了它

最新更新