线程互斥

多线程运行时,通常会访问同一个变量,同一个数据结构,或者同一段代码。因此,需要使用互斥技术来保护上述资源,确保多线程执行的正确性。

注:

我们通常说某个函数是线程安全的,也就是因为该函数实现加入了线程互斥保护。

4.1、QMutex

QMutex ( RecursionMode mode = NonRecursive )

~QMutex ()

void

lock ()

mutex加锁,如果当前其他线程已对该mutex加锁了,则该调用被阻塞直到其他线程释放该mutex。

bool

tryLock ()

mutex加锁,和lock不同的是,如果当前其他线程已对该mutex加锁了,则该调用会立即返回,而不被阻塞。

bool

tryLock ( int timeout )

同tryLock,和tryLock不同的是,如果当前其他线程已对该mutex加锁了,则该调用会等待一段时间,直到超时或者其他线程释放了mutex。

void

unlock ()

mutex解锁,释放被锁住的资源。

Mutex有两种模式,用户可以在构造函数参数中指定。

Constant

Value

Description

QMutex::Recursive

1

In this mode, a thread can lock the same mutex multiple times and the mutex won't be unlocked until a corresponding number of unlock() calls have been made.

该模式下,一个线程可以对mutex多次lock,直到相应次数的unlock,调用后,该mutex才真正被unlock。

QMutex::NonRecursive

0

In this mode, a thread may only lock a mutex once.

该模式下,mutex只能被lock一次。

实例:

QMutex mutex;
 int number = 6;
 
 void method1()
 {
     mutex.lock();
     number *= 5;
     number /= 4;
     mutex.unlock();
 }
 
 void method2()
 {
     mutex.lock();
     number *= 3;
     number /= 2;
     mutex.unlock();
 }

4.1、QMutexLocker

QMutexLocker ( QMutex * mutex )

~QMutexLocker ()

QMutex *

mutex () const

void

relock ()

void

unlock ()

QMutexLocker实际上是对QMutex使用的一种简化。

例如以下场景:

当某段代码存在多个分支,在对QMutex加锁后,需要在不同的分支路径下都执行解锁操作,才能保证Mutex关联的资源能被其他线程继续访问,       否则就出现死锁。

QMutexLocker接收一个QMutex作为参数,当创建QMutexLocker对象时,就对关联的Mutex进行了Lock操作,直到该QMutexLocker对象被销毁,相关的QMutex才被Unlock。

实例:

直接使用QMutex:

int complexFunction(int flag)
 {
     mutex.lock();
 
     int retVal = 0;
 
     switch (flag) {
     case 0:
     case 1:
         mutex.unlock();
         return moreComplexFunction(flag);
     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:

int complexFunction(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;
 }

当然,使用QMutexLocker时,也需要注意QMutexLocker对象的生存周期,否则可能会出现锁时间过长,或者锁住的资源过多。

4.3、QReadLocker、QWriteLocker、QReadWriteLocker

还有一种场景,我们所保护的资源是具有读写权限的,多个线程可以同时读取某个资源,但是当存在写操作,写操作未完成时,就不允许其他线程对该资源进行读操作。

QReadWriteLock ()

QReadWriteLock ( RecursionMode recursionMode )

~QReadWriteLock ()

void

lockForRead ()

void

lockForWrite ()

bool

tryLockForRead ()

bool

tryLockForRead ( int timeout )

bool

tryLockForWrite ()

bool

tryLockForWrite ( int timeout )

void

unlock ()

QReadLocker ( QReadWriteLock * lock )

~QReadLocker ()

QReadWriteLock *

readWriteLock () const

void

relock ()

void

unlock ()

QWriteLocker ( QReadWriteLock * lock )

~QWriteLocker ()

QReadWriteLock *

readWriteLock () const

void

relock ()

void

unlock ()

实例:

QReadWriteLock lock;
 
 void ReaderThread::run()
 {
     ...
     lock.lockForRead();
     read_file();
     lock.unlock();
     ...
 }
 
 void WriterThread::run()
 {
     ...
     lock.lockForWrite();
     write_file();
     lock.unlock();
     ...
 }

4.4、QSemaphore

和QMutex不同的是,QSemaphore一次可以对多个资源进行保护,

例如以下场景:

某工厂只有固定仓位,生产人员每天生产的产品数量不一,销售人员每天销售的产品数量也不一致。当生产人员生产P个产品时,就一次需要P个仓位,当销售人员销售C个产品时,就要求仓库中有足够多的产品才能销售。

如果剩余仓位没有P个时,该批次的产品都不存入,当当前已有的产品没有C个时,就不能销售C个以上的产品,直到新产品加入后方可销售。

这就是典型的生产者-消费者问题。

QSemaphore ( int n = 0 )

~QSemaphore ()

void

acquire ( int n = 1 )

int

available () const

void

release ( int n = 1 )

bool

tryAcquire ( int n = 1 )

bool

tryAcquire ( int n, int timeout )

实例:

QSemaphore sem(5);      // sem.available() == 5   默认有5个产品
 
 sem.acquire(3);         // sem.available() == 2   销售3个产品,成功
 sem.acquire(2);         // sem.available() == 0  销售2个产品成功
 sem.release(5);         // sem.available() == 5  生产5个产品
 sem.release(5);         // sem.available() == 10 生产10个产品
 
 sem.tryAcquire(1);      // sem.available() == 9, returns true 消费1个产品,成功
 sem.tryAcquire(250);    // sem.available() == 9, returns false 企图销售250个产品,失败,因为当前只剩下14个产品

4.5、QWaitCondition

QT线程(二)---线程同步的更多相关文章

  1. C#中的线程(二) 线程同步基础

    1.同步要领 下面的表格列展了.NET对协调或同步线程动作的可用的工具:                       简易阻止方法 构成 目的 Sleep 阻止给定的时间周期 Join 等待另一个线程 ...

  2. 第二十篇 .NET高级技术之C#中的线程(二) 线程同步基础

    1.同步要领 下面的表格列展了.NET对协调或同步线程动作的可用的工具:                       简易阻止方法 构成 目的 Sleep 阻止给定的时间周期 Join 等待另一个线程 ...

  3. MFC线程(二):线程同步临界区CRITICAL SECTION

    当多个线程同时使用相同的资源时,由于是并发执行,不能保证先后顺序.所以假如时一个公共变量被几个线程同时使用会造成该变量值的混乱. 下面来举个简单例子. 假如有一个字符数组变量 char g_charA ...

  4. 【Qt开发】事件循环与线程 二

    事件循环与线程 二 Qt 线程类 Qt对线程的支持已经有很多年了(发布于2000年九月22日的Qt2.2引入了QThread类),Qt 4.0版本的release则对其所有所支持平台默认地是对多线程支 ...

  5. 【转】Qt事件循环与线程 二

    转自:http://blog.csdn.net/changsheng230/article/details/6153449 续上文:http://blog.csdn.net/changsheng230 ...

  6. Java线程:线程的同步与锁

    一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. public ...

  7. C#中的线程二(Cotrol.BeginInvoke和Control.Invoke)

    C#中的线程二(Cotrol.BeginInvoke和Control.Invoke) 原文地址:http://www.cnblogs.com/whssunboy/archive/2007/06/07/ ...

  8. Qt学习笔记 线程(一)

    Qt中的线程是与平台无关的 QThread 提供了创建一个新线程的方法 新建一个线程,继承QThread并重写它的run()当调用 start()函数时会调用重载的run()函数 例: #ifndef ...

  9. 基础学习day11--多线程一线程的创建,运行,同步和锁

    一.线程基本概述 1.1.进程和线程 进程:一个应用程序一般都是一个进程,正在进行的程序 每一个进程最少都有一个线程,都有一个执行顺序,该顺序是一个执行路径或者一个控制单元 线程:进程中一个独立的控制 ...

  10. (转)Java线程:线程的同步与锁

      Java线程:线程的同步与锁       一.同步问题提出   线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Fo ...

随机推荐

  1. HDU4739Zhuge Liang's Mines(状压)

    链接 预处理出只有四个1的情况存入数组中 然后状压下 #include <iostream> #include<cstdio> #include<cstring> ...

  2. [Hadoop源码解读](六)MapReduce篇之MapTask类

    MapTask类继承于Task类,它最主要的方法就是run(),用来执行这个Map任务. run()首先设置一个TaskReporter并启动,然后调用JobConf的getUseNewAPI()判断 ...

  3. Journal.Today 1.0.0

    我喜欢把一天的日记都放在一个文件中,而不是每次都新建一个文件.   写了一个脚本,检测当天是否已经新建日记,已经新建则打开,未新建则新建并打开.   其中我不太喜欢Wiz日记本来的标题命名方式,所以都 ...

  4. MongoDB之四( 索引操作)

    我们日常做开发都避免不了要对程序进行性能优化,而程序的操作无非就是CURD,通常我们 又会花费50%的时间在R上面,因为Read操作对用户来说是非常敏感的,处理不好就会被人唾弃,呵呵. 从算法上来说有 ...

  5. RegisterFunction z

    #region SolidWorks Registration [ComRegisterFunctionAttribute] public static void RegisterFunction(T ...

  6. [BILL WEI] SQL 巧用临时表

    在写sql 的时候,我们有时候,只需要拿出某些特定的数据,去跟一些表进行关联,但是如果直接通过表关联的话,可能拿到 的数据有冗余,这个时候,我们就可以巧用临时表,讲自己需要的特定数据筛选出来,然后组成 ...

  7. LittleTools之网格输出为模型

    我经常要在Unity中生成一些网格,但是这些网格需要交给美工修改,所以又要将网格输出为模型.于是就有了下面的代码: using UnityEngine; using UnityEditor; usin ...

  8. 【HTML】Advanced2:Conditional Comments

    1.try and figure out what is sensible for you to support. Are your web site visitors likely to be us ...

  9. noip 2014 子矩阵

    先枚举行再DP列.好题,详见代码 #include <cstdio> #include <cstring> #include <cstdlib> #include ...

  10. 使用Windows Azure创建Windows系统虚拟机-上

    创建虚拟机来运行Windows 本教程介绍了如何轻松创建运行Windows 的 Azure虚拟机(VM),用作来自Azure管理门户中映像图库的Windows 服务器映像.映像图库提供了多种图像,包括 ...