QWaitCondition詳解

一、QWaitCondition簡介

QWaitCondition是Qt中的一個同步原語,它是一個條件變數(condition variable)對象,允許線程等待某些條件變為滿足後再繼續執行。在多線程編程中,條件變數經常與鎖(mutex)配合使用,通常是用於實現生產者-消費者模式,讀者-寫者模式等。QWaitCondition類可以用來實現上述模式,而不需要線程輪詢或者繁瑣的信號機制。

QWaitCondition類通常與QMutex類一起使用,QMutex用於保護共享資源的臨界區,QWaitCondition用於同步線程。QWaitCondition由兩個方法來實現條件等待:

  • wait(QMutex *mutex, ulong time = ULONG_MAX)  — 等待條件變數,釋放mutex鎖。
  • wakeOne()  — 喚醒等待條件變數的一個線程。
  • wakeAll()  — 喚醒等待條件變數的所有線程。

二、QWaitCondition的使用

1. 簡單使用

下面的例子展示了兩個線程來訪問共享變數,QWaitCondition用於同步這兩個線程。在示例中,首先創建一個Waiter類來等待條件變數,然後創建一個Notifier類來發出條件變數信號。

Waiter.h

“`cpp
#ifndef WAITER_H
#define WAITER_H

#include
#include
#include

class Waiter : public QThread
{
Q_OBJECT
public:
Waiter(QMutex* mutex, QWaitCondition* cond) :
m_mutex(mutex), m_cond(cond)
{}

void run() override
{
qDebug() <lock();
m_cond->wait(m_mutex);
qDebug() <unlock();
}

private:
QMutex* m_mutex;
QWaitCondition* m_cond;
};

#endif // WAITER_H
“`

Notifier.h

“`cpp
#ifndef NOTIFIER_H
#define NOTIFIER_H

#include
#include
#include

class Notifier : public QThread
{
Q_OBJECT
public:
Notifier(QMutex* mutex, QWaitCondition* cond) :
m_mutex(mutex), m_cond(cond)
{}

void run() override
{
m_mutex->lock();
QThread::sleep(1);
qDebug() <wakeOne(); // 喚醒所有waiter等待線程
m_mutex->unlock();
}

private:
QMutex* m_mutex;
QWaitCondition* m_cond;
};

#endif // NOTIFIER_H
“`

main函數

“`cpp
#include
#include

#include “Waiter.h”
#include “Notifier.h”

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

QMutex mutex;
QWaitCondition cond;

Waiter *waiter1 = new Waiter(&mutex, &cond);
Waiter *waiter2 = new Waiter(&mutex, &cond);
Notifier *notifier = new Notifier(&mutex, &cond);

waiter1->start();
waiter2->start();
notifier->start();

waiter1->wait();
waiter2->wait();
notifier->wait();

delete waiter1;
delete waiter2;
delete notifier;

return a.exec();
}
“`

在這個例子中,我們首先創建了一個Mutex和WaitCondition,然後創建了兩個Waiter線程和一個Notifier線程。兩個Waiter線程都在等待條件變數,等待Notifier線程發出條件變數信號後才會繼續執行。Notifier線程可以通過調用wakeOne(或wakeAll)方法來喚醒等待線程。在釋放鎖之前,wait()會一直等待。

2. 複雜使用

這裡使用一個模擬生產者和消費者的例子。主線程是消費者,生產者線程非同步地生產數據,並在隊列 ready 中放入數據,消費者線程在隊列 ready 中取出數據並處理。

producer.h

“`cpp
#ifndef PRODUCER_H
#define PRODUCER_H

#include
#include
#include
#include
#include
#include

class Producer : public QThread
{
Q_OBJECT
public:
explicit Producer(QMutex *mutex, QWaitCondition *cond, QObject *parent = nullptr): QThread (parent),
m_mutex(mutex), m_condition(cond), m_ready(nullptr), m_running(false)
{}

void run() override
{
QMutexLocker locker(m_mutex);
m_running = true;

while(m_running)
{
if(m_ready && m_ready->count() enqueue(value);
qDebug() << "producer enqueue" <wakeAll();
}
}

void stop(void) {m_running = false;}
void setReady(QQueue *ready) {m_ready = ready;}

private:
void lock_wait(int ms)
{
m_mutex->unlock();
QThread::msleep(ms);
m_mutex->lock();
}

private:
QMutex *m_mutex;
QWaitCondition *m_condition;
QQueue *m_ready;
bool m_running;
};

#endif // PRODUCER_H
“`

consumer.h

“`cpp
#ifndef CONSUMER_H
#define CONSUMER_H

#include
#include
#include
#include
#include
#include

class Consumer : public QThread
{
Q_OBJECT
public:
explicit Consumer(QMutex *mutex, QWaitCondition *cond, QObject *parent = nullptr): QThread (parent),
m_mutex(mutex), m_condition(cond), m_ready(nullptr), m_running(false)
{}

void run() override
{
QMutexLocker locker(m_mutex);
m_running = true;

while(m_running)
{
while(m_ready && m_ready->count() == 0)
{
m_condition->wait(m_mutex);
}

int value = 0;
while (m_ready && m_ready->count() > 0) {
value = m_ready->dequeue();
qDebug() << "consumer dequeue" << value;
}

// 模擬消費者消費的時間
lock_wait(100);
}
}

void setReady(QQueue *ready) {m_ready = ready;}
void stop(void) {m_running = false;}

private:
void lock_wait(int ms)
{
m_mutex->unlock();
QThread::msleep(ms);
m_mutex->lock();
}

private:
QMutex *m_mutex;
QWaitCondition *m_condition;
QQueue *m_ready;
bool m_running;
};

#endif // CONSUMER_H
“`

main函數

“`cpp
#include
#include

#include “producer.h”
#include “consumer.h”

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

QMutex mutex;
QWaitCondition condition;
QQueue ready;

Producer *producer = new Producer(&mutex, &condition);
Consumer *consumer = new Consumer(&mutex, &condition);

producer->setReady(&ready);
consumer->setReady(&ready);

producer->start();
consumer->start();

QThread::sleep(5);
producer->stop();
consumer->stop();

producer->wait();
consumer->wait();

delete producer;
delete consumer;

return a.exec();
}
“`

在這個例子中,我們有一個 Producer 線程通過非同步的方式生成數據,並將它們存儲在隊列 ready 中。一個 Consumer 線程從隊列 ready 中獲取數據並處理。

結語

本文簡要介紹了QWaitCondition的基本知識和使用方法。QWaitCondition可以與QMutex一起使用來實現線程同步,在多個線程之間進行通信。正如上述示例中的生產者-消費者模型,它可以幫助我們更輕鬆地編寫多線程程序來實現複雜的操作。但是,在使用QWaitCondition時,我們需要注意避免死鎖的問題。同時,由於QWaitCondition所使用的是條件變數,所以需要注意沒有競爭條件出現。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/283312.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-22 08:07
下一篇 2024-12-22 08:07

相關推薦

  • 神經網路代碼詳解

    神經網路作為一種人工智慧技術,被廣泛應用於語音識別、圖像識別、自然語言處理等領域。而神經網路的模型編寫,離不開代碼。本文將從多個方面詳細闡述神經網路模型編寫的代碼技術。 一、神經網…

    編程 2025-04-25
  • Linux sync詳解

    一、sync概述 sync是Linux中一個非常重要的命令,它可以將文件系統緩存中的內容,強制寫入磁碟中。在執行sync之前,所有的文件系統更新將不會立即寫入磁碟,而是先緩存在內存…

    編程 2025-04-25
  • Python安裝OS庫詳解

    一、OS簡介 OS庫是Python標準庫的一部分,它提供了跨平台的操作系統功能,使得Python可以進行文件操作、進程管理、環境變數讀取等系統級操作。 OS庫中包含了大量的文件和目…

    編程 2025-04-25
  • MPU6050工作原理詳解

    一、什麼是MPU6050 MPU6050是一種六軸慣性感測器,能夠同時測量加速度和角速度。它由三個感測器組成:一個三軸加速度計和一個三軸陀螺儀。這個組合提供了非常精細的姿態解算,其…

    編程 2025-04-25
  • 詳解eclipse設置

    一、安裝與基礎設置 1、下載eclipse並進行安裝。 2、打開eclipse,選擇對應的工作空間路徑。 File -> Switch Workspace -> [選擇…

    編程 2025-04-25
  • Python輸入輸出詳解

    一、文件讀寫 Python中文件的讀寫操作是必不可少的基本技能之一。讀寫文件分別使用open()函數中的’r’和’w’參數,讀取文件…

    編程 2025-04-25
  • Java BigDecimal 精度詳解

    一、基礎概念 Java BigDecimal 是一個用於高精度計算的類。普通的 double 或 float 類型只能精確表示有限的數字,而對於需要高精度計算的場景,BigDeci…

    編程 2025-04-25
  • nginx與apache應用開發詳解

    一、概述 nginx和apache都是常見的web伺服器。nginx是一個高性能的反向代理web伺服器,將負載均衡和緩存集成在了一起,可以動靜分離。apache是一個可擴展的web…

    編程 2025-04-25
  • C語言貪吃蛇詳解

    一、數據結構和演算法 C語言貪吃蛇主要運用了以下數據結構和演算法: 1. 鏈表 typedef struct body { int x; int y; struct body *nex…

    編程 2025-04-25
  • Linux修改文件名命令詳解

    在Linux系統中,修改文件名是一個很常見的操作。Linux提供了多種方式來修改文件名,這篇文章將介紹Linux修改文件名的詳細操作。 一、mv命令 mv命令是Linux下的常用命…

    編程 2025-04-25

發表回復

登錄後才能評論