QWaitCondition 用于多线程的同步,一个线程调用QWaitCondition::wait() 阻塞等待,直到另一个线程调用QWaitCondition::wake() 唤醒才继续往下执行
QWaitCondition常用函数如下所示:
- bool QWaitCondition::wait(QMutex *lockedMutex, QDeadlineTimer deadline = QDeadlineTimer(QDeadlineTimer::Forever));
- //释放lockedMutex并等待一个等待条件。
- //如果lockedMutex未处于锁定状态,则行为为undefined。直到满足以下任一条件:
- //1.另一个线程使用wakeOne () 或wakeAll () 发出信号。在这种情况下,此函数将返回 true。
- //2.已达到deadline参数期限给定的最后期限,默认为QDeadlineTimer::Forever,那么等待将永远不会超时(必须发出信号wakeOne或者wakeAll),如果等待超时,此函数将返回 false。
-
-
-
-
- void QWaitCondition::wakeAll()
- //唤醒所有调用wait()进行等待的线程。线程被唤醒的顺序取决于操作系统的调度策略,无法控制或预测。
-
- void QWaitCondition::wakeOne()
- //随机唤醒一个调用wait()进行等待的线程。被唤醒的线程取决于操作系统的调度策略,无法控制或预测。
假如我们有3个子线程,run函数下执行:
- while(1) {
- mutex.lock(); // mutex是一个全局共享互斥量
- btnPressed.wait(&mutex); // btnPressed 为 QWaitCondition
- do_something();
- mutex.unlock();
- }
主界面有一个按键,当按下的时候就会激活3个子线程做相关事情操作,代码如下所示:
- onClicked() {
- btnPressed.wakeAll(); // 唤醒所有线程
- }
-
示例如下所示,头文件:
- extern QMutex mutex;
- extern QWaitCondition btnPressed;
-
- class Thread : public QThread
- {
- Q_OBJECT
- protected:
- void run() override{
- while(1) {
- mutex.lock();
- qDebug()<<"进入等待"<<_delayMs;
- btnPressed.wait(&mutex);
- qDebug()<<"已唤醒 进入延时"<<_delayMs;
- msleep(_delayMs);
- mutex.unlock();
-
- }
- }
- public:
- Thread(int delayMs, QObject *parent = nullptr) : QThread(parent), _delayMs(delayMs)
- { };
-
- private:
- int _delayMs;
- };
-
-
-
- class Widget : public QWidget
- {
- Q_OBJECT
-
- public:
- Widget(QWidget *parent = nullptr);
- ~Widget();
-
- private slots:
- void on_pushButton_clicked();
-
- private:
- Ui::Widget *ui;
-
-
- Thread* t1;
- Thread* t2;
- Thread* t3;
-
- };
源文件:
- QMutex mutex;
- QWaitCondition btnPressed;
-
- Widget::Widget(QWidget *parent)
- : QWidget(parent)
- , ui(new Ui::Widget)
- {
- ui->setupUi(this);
-
- t1 = new Thread(100);
- t2 = new Thread(2000);
- t3 = new Thread(3000);
- t1->start(QThread::NormalPriority);
- t2->start(QThread::NormalPriority);
- t3->start(QThread::NormalPriority);
-
-
- }
-
- Widget::~Widget()
- {
-
- delete ui;
- t1->quit();
- t2->quit();
- t3->quit();
-
- }
-
-
- void Widget::on_pushButton_clicked()
- {
- btnPressed.wakeAll();
- }
假如我们此时线程1正在do_something()时,主界面再次按下了按键,由于等待的三个线程有一个未完成,那么该三个线程将不会再次执行.
如果要要优化,可以使用计数器和QMutex来保护它来解决这个问题。
主界面需要将多个很长数据任务存到数据库中,为了避免阻塞主界面,假如,我们有一个数据库线程,一个主界面线程:
假如没有QWaitCondition,我们的数据库线程一般有两种方式:
有QWaitCondition后
主界面调用DbWorker线程的公共函数:
- bool DbWorker::enqueueTask(DataTask* task)
- {
- mutex.lock();
- taskQueue.enqueue(task); // 入队
- mutex.unlock();
-
- if(this->isRunning()) {
- waitc.wakeAll(); // 唤醒线程 waitc 为 QWaitCondition
- } else {
- this->start(QThread::HighPriority);
- }
- return true;
- }
数据库线程(DbWorker):
- void DbWorker::run()
- {
- QSqlDatabase* db = dbInit(); // 打开数据库
- while(true) {
- DataTask* task;
- if(taskQueue.count()) {
- mutex.lock();
- task = taskQueue.dequeue(); // 出队
- mutex.unlock();
-
- taskHandler(); // 进行任务处理,存到数据中
- task->deleteLater();
- } else {
- waitmutex.lock();
- unsigned long timeout = 10000;
- bool ret = waitc.wait(&_waitmutex, timeout); // 最多等待 10s,假如ret为true,则说明队列有新数据要处理了,如果为false则到时间了
- do_something();
- waitmutex.unlock();
-
-
- mutex.lock();
- if(!taskQueue.count()) {
- mutex.unlock();
- break; // 等待10后,队列还是没有数据,则退出了
- }
- mutex.unlock();
-
- }
- }
-
- if(db) { // 最后关闭数据
- delete db;
- db = nullptr;
- }
-
-
- }
未完待续,后面有其它案例再来追加.