Aqu*_*irl 7 c++ mutex pthreads
从这里:我定义的Mutex类中的逻辑错误以及我在生产者消费者程序中使用它的方式 - pthreads
你将引用(!)传递给你的互斥体类的方式显然是在寻找麻烦,它无视任何类型的封装.
为什么这是个问题?我应该通过值传递然后编写复制构造函数吗?
在这种情况下,缺少封装有什么危害呢?我应该如何封装什么?
传递对锁的引用是一个坏主意 - 你不"使用"锁,你只获得然后你还给它.移动它使得很难跟踪您对(关键)资源的使用.传递对互斥锁变量而不是锁定的引用可能并不是那么糟糕,但仍然会让人更难以知道程序的哪些部分可能会死锁,所以要避免它.
请用简单的语言解释一下 - 为什么传递引用是一个坏主意?
我认为这是糟糕的抽象以及糟糕的封装.a mutex通常是默认构造的,删除了复制构造函数,具有多个引用同一逻辑对象的互斥对象容易出错,即它可能导致死锁和其他竞争条件,因为程序员或读者可以认为它们是不同的实体.
此外,通过指定您正在使用的内部互斥体,您将公开线程的实现细节,从而打破Mutex类的抽象.如果你正在使用a pthread_mutex_t那么你很可能会使用内核线程(pthreads).
封装也被破坏,因为您的互斥锁不是单个封装的实体,而是分散到几个(可能是悬空的)引用中.
如果要将a封装pthread_mutex_t到类中,则可以这样做
class Mutex {
public:
void lock();
void unlock();
// Default and move constructors are good!
// You can store a mutex in STL containers with these
Mutex();
Mutex(Mutex&&);
~Mutex();
// These can lead to deadlocks!
Mutex(const Mutex&) = delete;
Mutex& operator= (const Mutex&) = delete;
Mutex& operator= (Mutex&&) = delete;
private:
pthread_mutex_t internal_mutex;
};
Run Code Online (Sandbox Code Playgroud)
互斥对象应在实现文件中声明的共享范围内共享,而不是在本地声明它并在函数中作为引用传递.理想情况下,您只需将参数传递给您需要的线程构造函数.将对象中声明的对象的引用传递给与所讨论的函数相同的"级别"(在这种情况下为线程执行)通常会导致代码中的错误.如果声明互斥锁的范围不再存在会发生什么?析构函数是否mutex会使互斥体的内部实现无效?如果互斥体通过传递进入整个其他模块并且该模块启动其自己的线程并认为互斥锁永远不会阻塞,会发生什么情况,这可能导致令人讨厌的死锁.
另外一个你想使用互斥锁移动构造函数的情况是在互斥工厂模式中,如果你想创建一个新的互斥锁你会进行函数调用,该函数会返回一个互斥量,然后你将它添加到你的列表中互斥体或传递给通过某种共享数据请求它的线程(前面提到的列表对于这个共享数据是一个好主意).然而,获得这样的互斥工厂模式可能非常棘手,因为您需要锁定对公共互斥体列表的访问.尝试它应该是一件有趣的事情!
如果作者的意图是避免全局范围,那么在实现文件中将其声明为静态对象应该是足够的抽象.
我将其提炼为单独的问题: 1. 什么时候通过引用传递任何对象是合适的?2. 什么时候共享互斥锁合适?
您将对象作为参数传递的方式反映了您希望如何在调用者和被调用者之间共享对象的生命周期。如果通过引用传递,则必须假设被调用方将仅在调用期间使用该对象,或者如果引用由被调用方存储,则被调用方的生命周期比引用短。如果处理动态分配的对象,您可能应该使用智能指针,它(除其他外)允许您更明确地传达您的意图(请参阅Herb Sutter对此主题的处理)。
应该避免共享互斥锁。无论是通过引用传递还是以任何其他方式传递,都是如此。通过共享互斥锁,对象允许自身在内部受到外部实体的影响。这违反了基本的封装并且是足够的理由。(有关封装的优点,请参阅有关面向对象编程的任何文本)。共享互斥锁的一个真正后果是死锁的可能性。
举这个简单的例子:
从设计的角度来看,为什么要共享互斥锁?互斥锁保护可能被多个线程访问的资源。该互斥锁应该隐藏(封装)在控制该资源的类中。互斥量只是此类保护资源的一种方式;它是一个只有类应该知道的实现细节。相反,共享控制资源的类的实例,并允许它以任何它想要的方式确保其自身内部的线程安全。