Qt线程同步

来源:互联网 发布:知乎搞笑问题 编辑:程序博客网 时间:2024/05/22 05:17

多线程有的时候是很有用的,但是在访问一些公共的资源或者数据时,需要进行同步,否则会使数据遭到破坏或者获取的值不正确。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的线程中的一个线程。这个被唤醒的线程依赖于操组系统的调度策略,并且不能被控制或预知。

0 0