C++ GUI QT 第4版 之线程(二) 线程的同步

来源:互联网 发布:软件开发项目管理流程 编辑:程序博客网 时间:2024/05/16 05:22

QT中提供了一下几个可以实现线程同步的类:

(1)QMutex 类


 QMutex  mutex定义一个互斥信号量,目的是保护对象,数据结构或代码段

  步骤: 

  mutex.lock();加锁

   临近资源,代码的处理

   mutex.unlock();解锁

QMutex提供了线程间的顺序访问。

QMutex的目的是保护一个对象、数据结构或者代码段,所以同一时间只有一个线程可以访问它。(类似java的"synchronized”关键字)。互斥量最好和QMutexLocker搭配使用,因为它使得互斥量的解锁和锁定变得相当一致。

比如:
 int number = 6;
 void method1()
 {
     number *= 5;
     number /= 4;
 }
 void method2()
 {
     number *= 3;
     number /= 2;
 }
如果这两个方法被顺序调用,那么结果如下:
 // method1()
 number *= 5;        // number is now 30
 number /= 4;        // number is now 7
 // method2()
 number *= 3;        // number is now 21
 number /= 2;        // number is now 10
如果两个方法被两个不同的线程同时调用,那么结果可能如下:
 // Thread 1 calls method1()
 number *= 5;        // number is now 30
 // Thread 2 calls method2().
 //Thread1很可能被操作系统强制睡眠,允许Thread2运行
 number *= 3;        // number is now 90
 number /= 2;        // number is now 45
 // Thread 1 finishes executing.
 number /= 4;        // number is now 11, instead of 10
如果我们加了互斥量将得到期望的结果:
 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();
 }
这样,任何时候就只能有一个线程可以修改数据。这虽是个简单的例子,但是它适用于任何需要按特定频率发生的情况。但你在一个线程中调用lock(),其它线程将会在同一地点试图调用lock()来阻塞,知道这个线程调用unlock()之后其它线程才会获得这个锁。lock()的一种非阻塞选择是tryLock()。

请参考: QMutexLocker, QReadWriteLock, QSemaphore, 和 QWaitCondition.

成员变量说明

enum QMutex::RecursionMode
         值                                                     说明
QMutex::Recursive            1           在该模式下,一个线程可以锁住同一互斥量多次,互斥量只有经过相同次的unlock()后才能释放。
QMutex::NonRecursive    0           在该模式下,一个互斥量只能被锁住一次。

请参考 QMutex().

成员函数说明

QMutex::QMutex ( RecursionMode mode = NonRecursive )
构造一个新的互斥量。互斥量创建时的初始状态是解锁状态。如果创建时的模式为QMutex::Recursive,一个线程可以锁住同一互斥量多次,直到互斥量只有经过相同次的unlock()后才能释放。互斥量创建时默认为QMutex::NonRecursive。
请参考 lock() 和 unlock().

QMutex::~QMutex ()
销毁一个互斥量
警告:销毁一个处于锁定状态的互斥量将导致不确定的后果

void QMutex::lock ()
锁定互斥量。如果另一个线程已经锁定该互斥量,那么这次调用将阻塞直到那个线程把它解锁。
如果它是递归互斥量,允许同一个线程多次调用该函数锁定该互斥量。如果不是递归互斥量,调用该函数将导致死锁。
请参考 unlock().

bool QMutex::tryLock ()
尝试锁定互斥量。如果获得该互斥量,返回true。如果另外的线程已经锁定该互斥量,那么函数立即返回,返回值为false。
当互斥量被锁定,必须通过unlock()解锁,这样其他的线程才能锁定该互斥量。
如果它是递归互斥量,允许同一个线程多次调用该函数锁定该互斥量。如果不是递归互斥量,调用该函数再次锁定互斥量,函数立即返回,返回值为false。
请参考lock() 和 unlock().

bool QMutex::tryLock ( int timeout )
这是tryLock 重载的函数
尝试锁定互斥量。如果获得互斥量,返回true,否则返回false。如果其他线程锁定该互斥量,该函数将等待该互斥量timeout微秒。
注意:传递一个负数作为时间参数的话,相当于调用lock()函数。
当互斥量被锁定,必须通过unlock()解锁,这样其他的线程才能锁定该互斥量。
如果它是递归互斥量,允许同一个线程多次调用该函数锁定该互斥量。如果不是递归互斥量,调用该函数再次锁定互斥量,函数立即返回,返回值为false。
请参考 lock() 和 unlock().

void QMutex::unlock ()
释放互斥量。如果在不是持有该互斥量的线程中调用unlock(),将导致一个错误。
释放一个未被锁定的互斥量将导致一个不确定的后果。
请参考 lock().


(2)QMutexLocker类     QMutexLocker用来简化互斥量的锁定和解锁操作。

QMutexLocker用来简化互斥量的锁定和解锁操作。

在复杂函数或者异常处理代码中互斥量的锁定和解锁容易出错和难以调试。QMutexLocker就可以应用于这些情况,确保互斥量状态总是定义明确。
应该在程序中QMutex需要被锁定处创建QMutexLocker。
当QMutexLocker被创建后,互斥量就锁定了。你可以使用unlock()和relock()来解锁和再次锁定互斥量。如果互斥量被锁定,当QMutexLocker销毁的时候,自动实现互斥量的解锁。

例如:在这个函数中,进入函数开始处锁定互斥量,在各个退出点解锁互斥量
 int complexFunction(int flag)
 {
     mutex.lock();
     int retVal = 0;
     switch (flag) {
     case 0:
     case 1:
         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大大地简化代码,增加代码可读性
 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 是个局部变量,当函数返回时它就被销毁)。
这原则同样适用于抛出和捕获异常的代码。在异常处理函数中,如果互斥量已经被锁定,但是在异常被抛出之前还没有其他的地方解锁互斥量,那么异常将不被捕获。

QMutexLocker也提供了一个mutex()成员函数返回QMutexLocker操作的互斥量。对于需要访问互斥量是十分有用的,比如QWaitCondition::wait()。
例如:
 class SignalWaiter
 {
 private:
     QMutexLocker locker;

 public:
     SignalWaiter(QMutex *mutex): locker(mutex)
     {
     }

     void waitForSignal()
     {
         ...
         while (!signalled)
             waitCondition.wait(locker.mutex());
         ...
     }
 };
请参考 QReadLocker, QWriteLocker, 和 QMutex.

成员函数说明
QMutexLocker::QMutexLocker ( QMutex * mutex )
构造一个QMutexLocker对象并锁住互斥量。当QMutexLocker被销毁的时候,互斥量将被自动解锁。如果互斥量为0,QMutexLocker不起作用。
请参考 QMutex::lock().

 

QMutexLocker::~QMutexLocker ()
销毁QMutexLocker并且解锁在它构造函数中被锁定互斥量
请参考QMutex::unlock().

 

QMutex * QMutexLocker::mutex () const
返回在QMutexLocker构造函数中被锁定的互斥量

 

void QMutexLocker::relock ()
锁定一个解锁状态的互斥量
请参考 unlock().

 

void QMutexLocker::unlock ()
解锁互斥量。你可以使用relock()再次锁定它。当销毁的时候互斥量不应该被锁定。
请参考 relock()

(3). 

QSemaphore

QSemaphore提供了一个通用的可计数的信号量。

信号量实际上就是广义的互斥量。一个互斥量只能被锁定一次,然而一个信号量可以被获取多次。信号量常被用于保护一定数目的同类资源。

信号量支持两种基本的操作:acquire()和 release()

acquire(n)尝试获取n个资源。如果没有这么多数目的资源可用,它就阻断调用它的程序直到资源可用。

release(n)释放n个资源tryAcquire()尝试获取资源,如果资源数目不够就立即返回。available()函数用于返回可用资源的数目。

例子:

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

信号量的一个典型的应用就是用来控制环形缓冲区的读写,这个环形缓冲区被生产者和消费者共同访问。这个信号量的例子演示了如何应用信号量来解决该问题。

一个关于信号量的非计算机的问题就是餐厅问题。信号量的初始值为餐厅凳子总数目。当有客人到,他就需要凳子。当凳子被坐下,可用的凳子数就减少。当客人离开,可用的凳子数就增加,就允许更多的人进入。如果有10个人需要凳子,但是只有9张凳子,那么这10个人将等待。如果有4个人需要凳子,他们可以直接坐下(这样就只剩下5张凳子空着,那10个人将等得更久)。

请参考 QMutex, QWaitCondition, QThread, and Semaphores Example.

成员函数说明

QSemaphore::QSemaphore ( int n = 0 )

新建一个信号量,守护的资源数为n(默认为0)

请参考 release() and available().

 

 QSemaphore::~QSemaphore ()

销毁信号量警告:销毁一个正在使用的信号量将导致一个不确定的行为。

 

 void QSemaphore::acquire ( int n = 1 )

从该信号量守护的资源中获取n个资源。如果获取的资源数目大于available()返回值,那么该调用将阻塞,直到可获取足够的资源数。

请参考 release(), available(), and tryAcquire().

 

 int QSemaphore::available () const

返回当前信号量可以使用的资源的数目。该数不能为负。

请参考 acquire() and release().

 

 void QSemaphore::release ( int n = 1 )

释放n个信号量守护的资源给信号量。该函数也可以用来增加一个信号量可使用的资源数目,例如

QSemaphore sem(5); // a semaphore that guards 5 resources

sem.acquire(5); // acquire all 5 resources

sem.release(5); // release the 5 resources

sem.release(10); // "create" 10 new resources

请参考 acquire() and available().

 

 bool QSemaphore::tryAcquire ( int n = 1 )

尝试从信号量守护的资源中获取n个资源。成功,则返回true。如果当前可用的资源数目不够立即返回,返回值为false,并且不占用任何资源。例如:

QSemaphore sem(5); // sem.available() == 5

sem.tryAcquire(250); // sem.available() == 5, returns false

sem.tryAcquire(3); // sem.available() == 2, returns true

请参考 acquire().

 

 bool QSemaphore::tryAcquire ( int n, int timeout )

尝试从信号量守护的资源中获取n个资源。成功,则返回true。如果当前可用的资源数目available()不够,将等待timeout微秒。

注意:超时参数timeout为负数的话,就等同于调用acquire()函数。例如:

QSemaphore sem(5); // sem.available() == 5

sem.tryAcquire(250, 1000); // sem.available() == 5, waits 1000 milliseconds and returns false

sem.tryAcquire(3, 30000); // sem.available() == 2, returns true without waiting

请参考 acquire().



0 0
原创粉丝点击