QMutex & QWaitCondition 小记!

QT互斥锁与条件变量
本文通过两个示例线程CA和CB演示了QT中互斥锁与条件变量的使用方式。CA线程锁定互斥锁并唤醒所有等待线程,而CB线程则在接收到信号后解除阻塞状态并执行任务。

用惯了Boost的mutex和Condition,突然发现QT的互斥锁与信号量跟Boost的这么神似! 所以今儿将QT的互斥与信号量做个小抄,方便后续查看!

同时,记录一下先前对condition的使用一直比较模糊的地方顺便也做个笔记!

// QThreadAndMutexLocker.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include <iostream>
#include <QtCore\qthread>
#include <QtCore\qmutex>
#include <QtCore\qwaitcondition>

QMutex g_mutex;
QWaitCondition g_cond;


class CA : public QThread
{
public:
	CA(){ start(); }
	~CA(){ wait(); }

private:
	void run()
	{
		for (size_t i = 0; i < 10; i++)
		{
			QThread::msleep(100);
			QMutexLocker l(&g_mutex);
			std::cout << "CA locked and call wakeAll() " << i << std::endl;

			g_cond.wakeAll();

			/*以下注释部分证明g_cond.wakeAll()只是发出信号,但没有解锁,wakeAll下面代码部分仍是CA的锁范围之内
			注:因此g_cond.wakeAll调用之后,CB线程虽拿到信号但直到CA线程释放锁才行。
			结论:情况1:g_cond.wakeAll()在CA的锁范围之内, CA线程调用wakeAll(),CB拿到信号并准备解锁,CA释放锁,CB拿到锁
			     情况2:g_cond.wakeAll()在CA的锁范围之外, CA线程调用wakeAll(),CB拿到信号并准备解锁,CA与CB产生抢锁
			    因此:g_cond.wakeAll()即可在锁范围内出现也可出现在锁范围外,但是,场景1的明显效率高于场景2。如果在CA
				的锁范围外调用一次sleep,能更能有效的确保CB能第1时间拿到锁。
			*/
			//for (size_t i = 0; i < 10; i++)
			//{
			//	std::cout << " CA : " << i << std::endl;
			//	QThread::msleep(1000);
			//}
		}

		QThread::msleep(1);  //等待1ms,让CA线程挂机,确保CB线程能即时拿到锁.
	}
};

class CB : public QThread
{
public:
	CB(){ start(); }
	~CB(){ wait(); }

private:
	void run()
	{

		for (size_t i = 0; i < 10; i++)
		{
			QMutexLocker l(&g_mutex);

			std::cout << " CB locked, and waitting condition!" << std::endl;

			g_cond.wait(&g_mutex); // 1: wait时,将调用g_mutex.unlock(); 2:等到信号时,将调用g_mutex.lock();

			for (size_t i = 0; i < 10; i++)
			{
				std::cout << " CB : " << i << std::endl;
				QThread::msleep(1000);
			}
		}
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
	{
		CB cb;
		QThread::msleep(2000);
		CA ca;
	}

	getchar();

	return 0;
}



Qt 多线程编程中,`QWaitCondition` 和 `QMutex` 通常配合使用,以实现线程间的高效同步。这种组合的核心机制是:一个线程在等待某个条件满足时,可以释放互斥锁并进入阻塞状态,直到另一个线程通知该条件已满足,等待线程才会被唤醒并重新尝试获取锁。 ### 使用原理 `QWaitCondition` 提供了 `wait()`、`wakeOne()` 和 `wakeAll()` 方法。其中,`wait(QMutex&amp;)` 方法会在等待前自动解锁互斥量,并在被唤醒后重新锁定互斥量;而 `wakeOne()` 和 `wakeAll()` 用于唤醒一个或所有等待中的线程。 `QMutex` 则用于保护共享资源,确保在任意时刻只有一个线程可以访问关键代码段或数据。 ### 使用步骤 1. 定义并初始化 `QMutex` 和 `QWaitCondition`。 2. 在等待条件的线程中,使用 `QMutex::lock()` 获取锁,然后检查是否满足继续执行的条件。 3. 如果条件不满足,调用 `QWaitCondition::wait(QMutex&amp;)`,这将自动释放锁并使线程进入等待状态。 4. 在另一个线程中,当条件满足时,调用 `QWaitCondition::wakeOne()` 或 `QWaitCondition::wakeAll()` 唤醒等待线程。 5. 等待线程被唤醒后会重新获取锁并检查条件是否满足,若满足则继续执行。 ### 示例代码 以下是一个简单的生产者-消费者模型,演示了 `QWaitCondition` 与 `QMutex` 的配合使用: ```cpp #include &lt;QThread&gt; #include &lt;QWaitCondition&gt; #include &lt;QMutex&gt; #include &lt;QQueue&gt; #include &lt;QDebug&gt; class ProducerConsumer : public QThread { public: ProducerConsumer(QObject *parent = nullptr) : QThread(parent), dataReady(false) {} void run() override { if (isProducer) { for (int i = 0; i &lt; 5; ++i) { mutex.lock(); queue.enqueue(i); qDebug() &lt;&lt; &quot;Produced:&quot; &lt;&lt; i; dataReady = true; condition.wakeOne(); // 唤醒等待的消费者线程 mutex.unlock(); msleep(500); } } else { for (int i = 0; i &lt; 5; ++i) { mutex.lock(); while (!dataReady) { condition.wait(&amp;mutex); // 释放锁并等待 } int value = queue.dequeue(); qDebug() &lt;&lt; &quot;Consumed:&quot; &lt;&lt; value; dataReady = false; mutex.unlock(); } } } void setIsProducer(bool isProd) { isProducer = isProd; } private: QMutex mutex; QWaitCondition condition; QQueue&lt;int&gt; queue; bool dataReady; }; int main(int argc, char *argv[]) { ProducerConsumer producer, consumer; producer.setIsProducer(true); consumer.setIsProducer(false); producer.start(); consumer.start(); producer.wait(); consumer.wait(); return 0; } ``` ### 注意事项 - 在调用 `QWaitCondition::wait()` 前,必须确保互斥量已经被锁定[^3]。 - 通常将 `wait()` 放在循环中,以防止虚假唤醒(Spurious Wakeup)。 - 使用 `wakeOne()` 可唤醒一个等待线程,而 `wakeAll()` 会唤醒所有等待线程。 - 若等待操作可能超时,可使用 `wait(QMutex&amp;, unsigned long)` 的带超时版本,并检查返回值。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值