也谈线程同步变量

  前段时间看了一下QT中的线程同步变量,这里谈谈我的心得,由于线程同步变量在很多系统和平台中都有涉及,所以下文将更多地从它们的共性方面去讨论。废话少说,进入正文。

QT中主要提供了以下几个同步类:

QMutex

QSemaphore

QWaitCondition

QReadLocker

QWriteLocker 等。

下面就主要探讨一下前三个同步类。

 QMutex类的功能比较简单,主要分为RecursiveNon-Recursive两种方式,Recursive模式即指在同一线程中可以对某个QMutex多次加锁,当然也要同等次数的解锁。而Non-Recursive即指QMutex只能被lock一次,否则视为死锁。下面重点介绍一下这两种模式的异同。

  在Recursive模式下需要记录第一次对其进行lock操作的线程id,即ownership(它的所属),如果是同一个线程,则对此Mutex再加一次锁,不会block当前的线程。如果是不同的线程,则需要block当前的线程,此外对该QMutex unlock的线程必须和lock的线程相同。

  在Non-Recursive模式下unlock的线程则不需要与lock的线程相同,在这点上有点类似于Semaphore。有的地方说Non-Recursive模式下的Mutex没有记录ownership,那么我猜测Non-Recursive死锁的检测可能就是等待确定的时间,如果超过了该时间没有被解锁则判定为死锁,不知道这种猜测对不对,哪位知道的可以share一下。通过查看QMutex的代码,发现QMutex版本的Mutex记录了线程的Ownership,所以在QMutex被二次加锁时能够立刻检查出死锁。

 

QSemaphore提供对多个资源的保护,适用于生产者和消费者的模式。包括AcquireRelease方法。

 

QWaitCondition,有些平台上也叫Monitor,提供了对多个条件的等待。下面是一个用条件变量实现的生产者和消费者的例子。

ExpandedBlockStart.gif 代码
void  Producer::run()
{
    qsrand(QTime( 0 , 0 , 0 ).secsTo(QTime::currentTime()));
   
    
for  ( int  i  =   0 ; i  <  DataSize;  ++ i) {
        mutex. lock ();
        
if  (numUsedBytes  ==  BufferSize)
            
// 缓冲区已满则等待
            bufferNotFull.wait( & mutex);
        mutex.unlock();
 
        buffer[i  %  BufferSize]  =   " ACGT " [( int )qrand()  %   4 ];
 
        mutex. lock ();
        
++ numUsedBytes;
        
// 生产完一个,唤醒因为缓冲区为空而等待的消费者
        bufferNotEmpty.wakeAll();
        mutex.unlock();
    }
}
// ! [2]
 
// ! [3]
class  Consumer :  public  QThread
// ! [3]  // ! [4]
{
public :
    
void  run();
};
 
void  Consumer::run()
{
    
for  ( int  i  =   0 ; i  <  DataSize;  ++ i) {
        mutex. lock ();
        
if  (numUsedBytes  ==   0 )
            
// 缓冲区为空,等待
            bufferNotEmpty.wait( & mutex);
        mutex.unlock();
       
        fprintf(stderr,  " %c " , buffer[i  %  BufferSize]);
 
        mutex. lock ();
        
-- numUsedBytes;
        
// 消费完一个,唤醒因为缓冲区满等待的消费者
        bufferNotFull.wakeAll();
        mutex.unlock();
    }
    fprintf(stderr,  " \n " );
}

 

 

由此看出WaitCondition是与一个条件联系在一起用的,常见的使用方法如下:

ExpandedBlockStart.gif 代码
While( true )
{
    Do_something();
    Mutex. lock ();
    If (condition)
        waitCondition.wati(mutex);
    Mutex.unlock();
}

While ( true )
{
    Do_something();
    Mutex. lock ();
    Chang the condtion;
    waitCodtion.wake();
    Mutex.unlock();
}

 

 

Note:WaitConditionwait的内部实现一般是先unlock传入的mutex,然后等待,收到信号后再对mutex进行lock操作。这样为编程提供了一些便利,下面将提到。

 

SemaphoreWaitCondition的比较

下表总结了两者的不同 

SemaphoresCondition Variables
Can be used anywhere in a program, but should not be used in a monitorCan only be used in monitors
Wait() does not always block the caller (i.e., when the semaphore counter is greater than zero).Wait() always blocks the caller.
Signal() either releases a blocked thread, if there is one, or increases the semaphore counter.Signal() either releases a blocked thread, if there is one, or the signal is lost as if it never happens.
If Signal() releases a blocked thread, the caller and the released thread both continue.If Signal() releases a blocked thread, the caller yields the monitor (Hoare type) or continues (Mesa Type). Only one of the caller or the released thread can continue, but not both.

 总得来说两者是可以互换的,比如WaitCondition虽然没有记录在它上面等待的个数,但是可以通过与一个整数联用来达到与Semaphore相同,如上面的例子中所示。据说WaitCondition内部也可以用Semaphore来实现的。

此外在使用WaitCondition的时候要注意的是WaitConditionwake方法在没有其他线程等待的情况下是不起作用的,所以在使用的时候要格外注意,下面是一个qt中的一个例子:

ExpandedBlockStart.gif 代码
forever {
     mutex. lock ();
     keyPressed.wait( & mutex);
     
++ count;
     mutex.unlock();
 
     do_something();
 
     mutex. lock ();
     
-- count;
     mutex.unlock();
 }
forever {
     getchar();
 
     mutex. lock ();
     
//  Sleep until there are no busy worker threads
      while  (count  >   0 ) {
         mutex.unlock();
         sleep( 1 );
         mutex. lock ();
     }
     keyPressed.wakeAll();
     mutex.unlock();
 }

 

 

上面的代码通过添加一个count变量,防止在其他线程还在工作的情况下被唤醒。由于此处的mutex还用来保护count变量的同步,所以如果QWaitCondition在wait操作时没有对其unlock,那么在另一个线程中想对其操作,满足原来的条件的时候就会产生死锁。 

转载于:https://www.cnblogs.com/chinese-zmm/archive/2010/04/08/1707793.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值