多线程有的时候是很有用的,但是在访问一些公共的资源或者数据时,需要进行同步,否则会使数据遭到破坏或者获取的值不正确。Qt提供了一些类来实现线程的同步,如QMutex,QMutexLocker,QReadWriteLock,QReadLocker,QWriteLocker,QSemaphore和QWaitCondition。
QMutex 互斥量
QMutex ( RecursionMode mode = NonRecursive )。
RecursionMode 递归模式。枚举类型RecursionMode 有两个值:
QMutex::Recursive,在这个模式下,同一个线程可以多次锁同一个互斥量。需要注意的是,调用lock()多少次锁,就必须相应的调用unlock()一样次数解锁。
QMutex::NonRecursive(默认),在这个模式下,一个线程只能锁互斥量一次。
void QMutex::lock ()
该函数用来锁住一个互斥量。如果另外的线程已经锁住了互斥量,函数将被阻塞等待另外的线程解锁互斥量。
如果是一个可递归的互斥量,则可以从同一个线程多次调用这个函数,如果是非递归的互斥量,多次调用这个函数将会引发死锁。
bool QMutex::tryLock ()
该函数试图锁一个互斥量,如果成功则返回true。如果另外的线程已经锁住了互斥量,函数直接返回false,非阻塞。
void QMutex::unlock ()
该函数对互斥量进行解锁。如果在另外的线程加锁,尝试在别的线程进行解锁则会引发错误。试图对没有加锁的互斥量解锁结果是未定义的。
QMutexLocker(简化QMutex )
QmutexLocker只是为了简化我们队互斥量的加锁和解锁操作
QMutexLocker (QMutex * mutex )。
构造一个QMutexLocker对象并锁住互斥量。当QMutexLocker被销毁的时候,互斥量将被自动解锁。如果互斥量为0,QMutexLocker不起作用。
构造函数必须传入一个互斥量指针,在构造函数里mutex调用了lock()。
QMutex * QMutexLocker::mutex () const
返回在QMutexLocker构造函数中被锁定的互斥量
void QMutexLocker::relock ()
锁定一个解锁状态的互斥量
void QMutexLocker::unlock ()
解锁互斥量。你可以使用relock()再次锁定它。当销毁的时候互斥量不应该被锁定。
假设有个函数有很多return 语句,那么我们就必须记得在每个语句前unlock互斥量,否则互斥量将无法得到解锁,导致其他等待的线程无法继续执行。
int complexFunction(intflag)
{
mutex.lock();
int retVal = 0;
switch (flag) {
case 0:
case1:
retVal = moreComplexFunction(flag);
break;
case 2:
{
int status = anotherFunction();
if (status < 0) {
mutex.unlock();
return -2;
}
retVal = status + flag;
}
break;
default:
if (flag > 10) {
mutex.unlock();
return -1;
}
break;
}
mutex.unlock();
return retVal;
}
这样的代码显得很冗余又容易出错。如果我们用QMutexLocker
intcomplexFunction(int flag)
{
QMutexLocker locker(&mutex);
int retVal = 0;
switch (flag) {
case 0:
case 1:
return moreComplexFunction(flag);
case 2:
{
int status = anotherFunction();
if (status < 0)
return -2;
retVal = status + flag;
}
break;
default:
if (flag > 10)
return -1;
break;
}
return retVal;
}
由于locker 是局部变量,在离开函数作用域时,mutex肯定会被解锁。
QreadWriteLock 读写锁
QreadWriteLock是一个读写锁,主要用来同步保护需要读写的资源。当你想多个读线程可以同时读取资源,但是只能有一个写线程操作资源,而其他线程必须等待写线程完成时,这时候用这个读写锁就很有用了。QreadWriteLock也有递归和非递归模式之分。
void QReadWriteLock::lockForRead ()
该函数lock加了读操作的锁。如果有别的线程已经对lock加了写操作的锁,则函数会阻塞等待。
void QReadWriteLock::lockForWrite ()
该函数给lock加了写操作的锁,如果别的线程已经加了读或者写的锁,则函数会被阻塞。
void QReadWriteLock::unlock ()
解锁函数。
QReadWriteLock lock;
void ReaderThread::run()
{
lock.lockForRead();
read_file();
lock.unlock();
}
void WriterThread::run()
{
lock.lockForWrite();
write_file();
lock.unlock();
}
QReadLocker和QWriteLocker(简化QReadWriteLock)
当QReadLocker,QWriteLocker被销毁的时候,QReadWriteLock将被自动解锁。
QReadWriteLock lock ;
QByteArray readData()
{
lock.lockForRead();
...
lock.unlock();
return data;
}
//用下面的方式就可以简化了:
QReadWriteLock lock ;
QByteArray readData()
{
QReadLocker locker(&lock); //
...
return data;
}
QSemaphore 信号量
QSemaphore是提供一个计数的信号量。信号量是泛化的互斥量。一个信号量只能锁一次,但是我们可以多次获得信号量。信号量可以用来同步保护一定数量的资源。
QSemaphore ( int n = 0 )
建立对象时可以给它n个资源
void acquire ( int n = 1 )
这个操作一次减少n个资源,如果现有资源不到n个就会阻塞
int available () const
返回当前可用的QSemaphore资源个数
void release ( int n = 1 )
这个操作一次增加n个资源
bool tryAcquire ( int n = 1 )
类似于acquire,但是申请不到n个资源时不会阻塞会立即返回
bool tryAcquire ( int n, int timeout )
类似于acquire,但是申请不到n个资源时会阻塞等待资源可用,阻塞超过 timeout毫秒就返回。
Example:
QSemaphore sem(5); // sem.available() == 5
sem.acquire(3); // sem.available() == 2
sem.acquire(2); // sem.available() == 0
sem.release(5); // sem.available() == 5
sem.release(5); // sem.available() == 10
sem.tryAcquire(1); // sem.available() == 9, returns true
sem.tryAcquire(250); // sem.available() == 9, returns false
经典例子就是生产者-消费者
QWaitCondition 条件变量
QWaitCondition类提供了一个条件变量,它允许我们通知其他线程,等待的某些条件已经满足。等待QWaitCondition变量的可以是一个或多个线程。当我们用wakeOne()通知其他线程时,系统会随机的选中一个等待进行唤醒,让它继续运行。其实前面的信号量和读写锁内部实现都有用到QWaitCondition的。
QWaitCondition必须与QMutex或者QReadwriteLock一起用。
bool QWaitCondition::wait ( QMutex * mutex, unsigned long time =ULONG_MAX )
该函数对mutex解锁,然后等待。在调用这个函数之前,mutex必须是加锁状态。如果mutex没有加锁,则函数直接返回。如果mutex是可递归的,函数也直接返回。该函数对mutex解锁,然后等待,知道以下条件之一满足:
另外的线程调用wakeOne()或 wakeAll(),则该函数会返回true。
时间过了Time毫秒。如果time为ULONG_MAX(默认),则将会一直等待不会超时。如果超时则返回false。
bool QWaitCondition::wait ( QReadWriteLock * readWriteLock, unsigned long time =ULONG_MAX )
该函数对readWriteLock解锁并等待条件变量。在调用这个函数之前,readWriteLock必须是加锁状态的。如果不是加锁状态,则函数立即返回。readWriteLock必须不能是递归加锁的,否则将不能正确的解锁。返回的满足条件跟上面的函数一样。
void QWaitCondition::wakeAll ()
这将会唤醒所有等待QWaitCondition的线程。这些线程被唤醒的顺序依赖于操组系统的调度策略,并且不能被控制或预知。
void QWaitCondition::wakeOne ()
这将会唤醒所有等待QWaitCondition的线程中的一个线程。这个被唤醒的线程依赖于操组系统的调度策略,并且不能被控制或预知。

316

被折叠的 条评论
为什么被折叠?



