惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

小众软件
小众软件
Cloudbric
Cloudbric
G
Google Developers Blog
博客园_首页
博客园 - 司徒正美
N
Netflix TechBlog - Medium
Recorded Future
Recorded Future
博客园 - 叶小钗
C
Check Point Blog
L
LangChain Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
酷 壳 – CoolShell
酷 壳 – CoolShell
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Stack Overflow Blog
Stack Overflow Blog
大猫的无限游戏
大猫的无限游戏
Cyberwarzone
Cyberwarzone
Project Zero
Project Zero
V
Vulnerabilities – Threatpost
C
Cisco Blogs
Scott Helme
Scott Helme
Last Week in AI
Last Week in AI
博客园 - 聂微东
T
Threat Research - Cisco Blogs
www.infosecurity-magazine.com
www.infosecurity-magazine.com
B
Blog RSS Feed
Microsoft Security Blog
Microsoft Security Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Hacker News
The Hacker News
Forbes - Security
Forbes - Security
Simon Willison's Weblog
Simon Willison's Weblog
I
Intezer
Cisco Talos Blog
Cisco Talos Blog
S
Schneier on Security
T
The Exploit Database - CXSecurity.com
阮一峰的网络日志
阮一峰的网络日志
爱范儿
爱范儿
AWS News Blog
AWS News Blog
C
CERT Recently Published Vulnerability Notes
Google DeepMind News
Google DeepMind News
N
News | PayPal Newsroom
Help Net Security
Help Net Security
B
Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
Tenable Blog
I
InfoQ
S
Securelist
V
Visual Studio Blog
U
Unit 42
博客园 - 【当耐特】
S
Security @ Cisco Blogs

博客园 - chinese_submarine

QT项目性能调优小记 Windows下HG服务器的搭建 svn+tp-link+花生壳搭建外网服务器 udev介绍 极大极小博弈树的简洁(附Tic-Tac-Toe源码) 程序优化小记 Beating the Average------为什么要学习Lisp[转] 巧用qmake工具生成专业的makefile QT中拖拽的实现(附示例代码) 从QDataStream向QByteArray中写入数据时的注意点(QT) 如何保持GUI的响应流畅(QT平台) windows7到期的问题 简述FPS的计算方法 QT中的View Model模型系列一 从农夫养牛问题推广到斐波那契数列 TimeZoneChange事件的捕获 浏览器扩展系列————透明浏览器窗口的实现 浏览器扩展系列————异步可插入协议(pluggable protocol)的实现 浏览器扩展系列————给MSTHML添加内置脚本对象【包括自定义事件】
也谈线程同步变量
chinese_submarine · 2010-04-08 · via 博客园 - chinese_submarine

  前段时间看了一下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,提供了对多个条件的等待。下面是一个用条件变量实现的生产者和消费者的例子。

代码

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是与一个条件联系在一起用的,常见的使用方法如下:

代码

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的比较

下表总结了两者的不同 

Semaphores Condition Variables
Can be used anywhere in a program, but should not be used in a monitor Can 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中的一个例子:

代码

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,那么在另一个线程中想对其操作,满足原来的条件的时候就会产生死锁。