Qt线程同步

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值