Qt同步线程
我们知道,多线程有的时候是很有用的,但是在访问一些公共的资源或者数据时,需要进行同步,否则会使数据遭到破坏或者获取的值不正确。Qt提供了一些类来实现线程的同步,如QMutex,QMutexLocker,QReadWriteLock,QReadLocker,QWriteLocker,QSemaphore和QWaitCondition。下面我们分别来看它们的用法:
QMutex
首先,简单的了解一下QMutex提供的函数。
构造函数:QMutex ( RecursionMode mode = NonRecursive )。
需要注意的是构造函数的参数,RecursionMode 递归模式。枚举类型RecursionMode 有两个值:
QMutex::Recursive,在这个模式下,一个线程可以多次锁同一个互斥量。需要注意的是,调用lock()多少次锁,就必须相应的调用unlock()一样次数解锁。
QMutex::NonRecursive(默认),在这个模式下,一个线程只能锁互斥量一次。
void QMutex::lock ()
该函数用来锁住一个互斥量。如果另外的线程已经锁住了互斥量,函数将被阻塞等待另外的线程解锁互斥量。
如果是一个可递归的互斥量,则可以从同一个线程多次调用这个函数,如果是非递归的互斥量,多次调用这个函数将会引发死锁。我们来看看源码是怎么实现的。
void QMutex::lock()
{
QMutexPrivate *d = static_cast<QMutexPrivate*>(this->d);
Qt::HANDLE self;
if(d->recursive) {
self = QThread::currentThreadId();
if(d->owner == self) {
++d->count; //同一个线程多次lock时,仅仅自增count
//当然递归次数太多也会导致栈溢出
Q_ASSERT_X(d->count != 0, "QMutex::lock", "Overflowin recursion counter");
return;
}
boolisLocked = d->contenders.testAndSetAcquire(0, 1);
if(!isLocked) {
// didn'tget the lock, wait for it
isLocked = d->wait();
Q_ASSERT_X(isLocked, "QMutex::lock",
"Internalerror, infinite wait has timed out.");
}
d->owner = self; //递归模式时,owner记录拥有互斥量的线程
++d->count; //记录lock的次数
Q_ASSERT_X(d->count != 0, "QMutex::lock", "Overflowin recursion counter");
return;
}
//非递归模式时,
boolisLocked = d->contenders.testAndSetAcquire(0, 1); //尝试加锁
if(!isLocked) {
lockInternal(); //加锁失败则在lockInternal()中一直等到别的线程解锁。
}
}
看看lockInternal的实现
void QMutex::lockInternal()
{
。。。
do {
。。。。//其他代码太复杂,感觉最重要的就是这个while循环了,
//一直循环检测,试图加锁。这我们就好理解,非递归模式的//互斥量,不要在同一个线程里,多次调用lock了。因为第二次调用的时候会在
//这里死循环了
} while(d->contenders != 0 || !d->contenders.testAndSetAcquire(0, 1));
。。。。。。。
}
bool QMutex::tryLock ()
该函数试图锁一个互斥量,如果成功则返回true。如果另外的线程已经锁住了互斥量,函数直接返回false。
bool QMutex::tryLock ( int timeout )
该函数跟上面的trylock()相似。不同的是,如果互斥量在别的线程锁住的情况下,函数会等待timeout 毫秒。需要注意的是,如果传入的timeout 为负数,函数将无限期等待,跟调用lock()一样的效果。这个函数跟上面的差不多,