0CCh Blog

QMutex中的优化小细节

在这篇文章中,我们要讨论的并不是QMutex的原理或者是应该如何使用,而是QMutex中一个很少被提到的优化细节。

我们知道在QT中,QMutex通常会和QMutexLocker一起使用,主要的用途是保护一个对象、数据结构或代码段,以便一次只让一个线程可以访问它们。 这一点对于多线程程序来说非常重要,而如今的程序已经离不开多线程,所以QMutex的使用场景也是越来越多,为了提高QMutex的使用效率,QT在QMutex的实现上加入了一个优化细节,这是std::mutex没有的,让我们来看看这个优化具体是什么。

QMutex的基类QBasicMutex有一个类型为QBasicAtomicPointer<QMutexData>成员,这里可以先忽略QBasicAtomicPointer,它只是保证对指针的原子操作,正在发挥作用的是QMutexData*QMutexData类型也没有什么特殊之处,真正的优化是在它的派生类QMutexPrivate,来看一段QMutexPrivate的代码:

class QMutexPrivate : public QMutexData
{
...
void deref() {
if (!refCount.deref())
release();
}
void release();
...
};

void QMutexPrivate::release()
{
freelist()->release(id);
}

可以看到,当引用计数为0的时候调用的release函数并没有真正释放互斥体对象,而是调用了一个freelistrelease函数。追踪freelist()会发现这样一段代码:

namespace {
struct FreeListConstants : QFreeListDefaultConstants {
enum { BlockCount = 4, MaxIndex=0xffff };
static const int Sizes[BlockCount];
};
const int FreeListConstants::Sizes[FreeListConstants::BlockCount] = {
16,
128,
1024,
FreeListConstants::MaxIndex - (16 + 128 + 1024)
};

typedef QFreeList<QMutexPrivate, FreeListConstants> FreeList;

static FreeList freeList_;
FreeList *freelist()
{
return &freeList_;
}
}

这下就豁然开朗了,QFreeList可以被认为是缓存池,用于维护QMutexPrivate的内存,当QMutexPrivate调用release函数的时候,QT并不会真的释放对象,而是将其加入到缓存池中,以便后续代码申请使用。这样不但可以减少内存反复分配带来的开销,也可以减少反复分配内核对象代码的开销,对于程序的性能是有所帮助的。

具体QFreeList的实现并不复杂,大家可以参考QT中的源代码qtbase\src\corelib\tools\qfreelist_p.h,另外除了QMutex以外,QRecursiveMutexQReadWriteLock也用到了相同的技术。