继承锁定而非组合锁定

3

在我编写或审核的大多数代码中,锁定是通过组合实现的,其中一个类拥有关键部分或互斥量:

class MyClass
{
    Mutex mMutex;
};

当可变成员可能通过多个线程访问时,我们通过 RAII 获取和释放锁,如下所示:

void MyClass::Method()
{
    Lock lock(mMutex);
    // ...
}

今天我审查了一些代码,其中使用继承实现锁,就像这样:
class MyClass : public Mutex
{
    // ...
};

而锁定是由类锁定“自身”执行的:

void MyClass::Method()
{
    Lock lock(this);
    // ...
}

这种方法有什么优点或缺点吗?还是只是一个风格问题?

你需要更加具体地说明“继承互斥量”的部分。它是从接口继承过来的还是真正的同步对象? - YeenFei
@YeenFei - 它是从一个真实(即具体)的同步对象继承而来的。 - LeopardSkinPillBoxHat
2个回答

6

这几乎没有意义。 MyClass 是一种扩展 Mutex 的同步对象吗?如果不是,那么继承几乎肯定是错误的选择,因为使用 MyClass 作为 Mutex 没有意义(MyClassMutex 的关系不是“is-a”关系)。

这也是危险的:

MyClass x;
Lock lock(x);
x.Method();   // uh oh.

请注意,在Win32世界中,一个线程可以多次获取锁定,所以不会出现“嗯哦”的情况。这似乎是一种明智的锁定方法,但这可能是因为我已经习惯了这种方法。 - Michael Burr
2
@Michael:一些平台/线程库同时拥有“Mutex”和“RecursiveMutex”,如果一个线程第二次锁定了Mutex,它将与自身死锁。我不知道这是否是一种常见的方法。 - James McNellis

4
私有继承可能更合适,因为私有继承更多地是实现组合的一种方式。但是,除非需要从Mutex继承(例如,如果Mutex有受保护的成员需要被访问),否则我认为更标准的组合方法——将其作为一个成员——可能会导致更少的混淆(比如人们想知道为什么使用继承而不是将Mutex作为一个成员)。
在某些情况下,公共继承可能是有意义的,例如您希望客户端能够以某种方式锁定该对象,那么他们可以简单地将MyClass视为Mutex。这可能不是一个很好的设计选择,因为如果以不可预期的方式锁定对象,则会打开更多死锁机会。如果Mutex保持私有(无论是通过继承还是通过标准组合),类可以更确定锁定的使用方式。
我不会感到惊讶,如果引入这个设计的人受到了.NET的影响,因为任何对象都是“可锁定”的。请注意,在.NET中,以前常用lock(this),但这种习惯用法已经不再流行,现在更正确的做法是有一个特定的私有对象成员来用于锁定。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接