Qt 異步編程模式與應用

在現代軟件開發中,異步編程已成為提升應用性能和響應性的關鍵技術。Qt 作為一個強大的跨平臺框架,提供了多種異步編程模式,包括信號槽機制、事件循環、線程池、異步 I/O 等。本文將深入探討 Qt 異步編程的各種模式及其應用場景,幫助開發者構建高效、響應迅速的應用程序。

一、信號槽機制

1. 基本信號槽用法
#include <QObject>
#include <QTimer>
#include <QDebug>class Producer : public QObject {Q_OBJECT
public:explicit Producer(QObject *parent = nullptr) : QObject(parent) {}public slots:void startProducing() {QTimer *timer = new QTimer(this);connect(timer, &QTimer::timeout, this, &Producer::produceData);timer->start(1000);  // 每秒觸發一次}signals:void dataReady(int value);private slots:void produceData() {static int counter = 0;emit dataReady(counter++);}
};class Consumer : public QObject {Q_OBJECT
public:explicit Consumer(QObject *parent = nullptr) : QObject(parent) {}public slots:void handleData(int value) {qDebug() << "Received data:" << value;}
};// 使用示例
void useSignalsAndSlots() {Producer producer;Consumer consumer;// 連接信號槽QObject::connect(&producer, &Producer::dataReady, &consumer, &Consumer::handleData);// 啟動生產者producer.startProducing();
}
2. 跨線程信號槽
#include <QThread>
#include <QDebug>class Worker : public QObject {Q_OBJECT
public:explicit Worker(QObject *parent = nullptr) : QObject(parent) {}public slots:void doWork() {for (int i = 0; i < 5; ++i) {QThread::sleep(1);emit resultReady(i);}emit finished();}signals:void resultReady(int value);void finished();
};class Controller : public QObject {Q_OBJECT
public:explicit Controller(QObject *parent = nullptr) : QObject(parent) {// 創建工作線程m_thread = new QThread(this);m_worker = new Worker();// 將 worker 移動到新線程m_worker->moveToThread(m_thread);// 連接信號槽connect(m_thread, &QThread::started, m_worker, &Worker::doWork);connect(m_worker, &Worker::resultReady, this, &Controller::handleResults);connect(m_worker, &Worker::finished, m_thread, &QThread::quit);connect(m_worker, &Worker::finished, m_worker, &QObject::deleteLater);connect(m_thread, &QThread::finished, m_thread, &QObject::deleteLater);// 啟動線程m_thread->start();}public slots:void handleResults(int value) {qDebug() << "Result received in controller:" << value;}private:QThread *m_thread;Worker *m_worker;
};

二、QtConcurrent 框架

1. 基本用法
#include <QtConcurrent>
#include <QFuture>
#include <QFutureWatcher>
#include <QDebug>// 耗時操作函數
int computeSum(int a, int b) {QThread::sleep(2);  // 模擬耗時操作return a + b;
}// 使用 QtConcurrent::run
void useQtConcurrentRun() {// 在線程池中運行函數QFuture<int> future = QtConcurrent::run(computeSum, 10, 20);// 可以繼續執行其他代碼...// 等待結果qDebug() << "Result:" << future.result();
}// 使用 QFutureWatcher 監控進度
void useFutureWatcher() {QFutureWatcher<int> *watcher = new QFutureWatcher<int>(this);// 連接信號槽connect(watcher, &QFutureWatcher<int>::finished, this, [watcher]() {qDebug() << "Computation finished. Result:" << watcher->result();watcher->deleteLater();});// 啟動任務QFuture<int> future = QtConcurrent::run(computeSum, 50, 60);watcher->setFuture(future);
}
2. 并行處理容器
// 處理函數
void processItem(int &item) {item = item * item;  // 處理每個元素
}// 并行處理容器
void parallelProcessContainer() {QList<int> list = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};// 并行處理每個元素QtConcurrent::map(list, processItem);// 或者使用 blockingMap 直接獲取結果QList<int> result = QtConcurrent::blockingMapped(list, [](int item) {return item * item;});qDebug() << "Processed list:" << result;
}

三、異步 I/O 操作

1. 異步文件操作
#include <QFile>
#include <QFileDevice>
#include <QDataStream>
#include <QDebug>void asyncFileRead() {QFile file("data.txt");if (!file.open(QIODevice::ReadOnly)) {qDebug() << "Failed to open file";return;}// 使用 QDataStream 進行異步讀取QDataStream stream(&file);// 讀取文件內容(異步操作)QByteArray data;stream >> data;// 處理讀取的數據qDebug() << "Read data size:" << data.size();file.close();
}void asyncFileWrite() {QFile file("output.txt");if (!file.open(QIODevice::WriteOnly)) {qDebug() << "Failed to open file";return;}QDataStream stream(&file);QByteArray data("Hello, World!");// 寫入數據(異步操作)stream << data;file.close();
}
2. 異步網絡操作
#include <QTcpSocket>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QDebug>// 異步網絡請求示例
void asyncNetworkRequest() {QNetworkAccessManager manager;QNetworkRequest request(QUrl("https://api.example.com/data"));// 發送異步請求QNetworkReply *reply = manager.get(request);// 連接響應信號QObject::connect(reply, &QNetworkReply::finished, [reply]() {if (reply->error() == QNetworkReply::NoError) {QByteArray data = reply->readAll();qDebug() << "Received data:" << data;} else {qDebug() << "Error:" << reply->errorString();}reply->deleteLater();});
}// 異步 TCP 通信示例
class AsyncTcpClient : public QObject {Q_OBJECT
public:explicit AsyncTcpClient(QObject *parent = nullptr) : QObject(parent) {m_socket = new QTcpSocket(this);// 連接信號槽connect(m_socket, &QTcpSocket::connected, this, &AsyncTcpClient::connected);connect(m_socket, &QTcpSocket::disconnected, this, &AsyncTcpClient::disconnected);connect(m_socket, &QTcpSocket::readyRead, this, &AsyncTcpClient::readyRead);connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error),this, &AsyncTcpClient::error);}void connectToHost(const QString &host, quint16 port) {m_socket->connectToHost(host, port);}void sendData(const QByteArray &data) {m_socket->write(data);}signals:void dataReceived(const QByteArray &data);private slots:void connected() {qDebug() << "Connected to server";}void disconnected() {qDebug() << "Disconnected from server";}void readyRead() {QByteArray data = m_socket->readAll();emit dataReceived(data);}void error(QAbstractSocket::SocketError socketError) {qDebug() << "Socket error:" << m_socket->errorString();}private:QTcpSocket *m_socket;
};

四、異步任務鏈與狀態機

1. 異步任務鏈
#include <QStateMachine>
#include <QState>
#include <QFinalState>
#include <QDebug>class AsyncTaskChain : public QObject {Q_OBJECT
public:explicit AsyncTaskChain(QObject *parent = nullptr) : QObject(parent) {// 創建狀態機m_machine = new QStateMachine(this);// 創建狀態QState *state1 = new QState(m_machine);QState *state2 = new QState(m_machine);QState *state3 = new QState(m_machine);QFinalState *finalState = new QFinalState(m_machine);// 設置狀態轉換connect(state1, &QState::entered, this, &AsyncTaskChain::task1);connect(state2, &QState::entered, this, &AsyncTaskChain::task2);connect(state3, &QState::entered, this, &AsyncTaskChain::task3);state1->addTransition(this, &AsyncTaskChain::task1Finished, state2);state2->addTransition(this, &AsyncTaskChain::task2Finished, state3);state3->addTransition(this, &AsyncTaskChain::task3Finished, finalState);// 設置初始狀態m_machine->setInitialState(state1);// 連接完成信號connect(m_machine, &QStateMachine::finished, this, &AsyncTaskChain::finished);}void start() {m_machine->start();}signals:void task1Finished();void task2Finished();void task3Finished();void finished();private slots:void task1() {qDebug() << "Task 1 started";// 模擬異步操作QTimer::singleShot(1000, this, &AsyncTaskChain::task1Finished);}void task2() {qDebug() << "Task 2 started";// 模擬異步操作QTimer::singleShot(1500, this, &AsyncTaskChain::task2Finished);}void task3() {qDebug() << "Task 3 started";// 模擬異步操作QTimer::singleShot(2000, this, &AsyncTaskChain::task3Finished);}private:QStateMachine *m_machine;
};
2. 使用 QPromise 和 QFuture
#include <QFuture>
#include <QFutureWatcher>
#include <QPromise>
#include <QtConcurrent>
#include <QDebug>// 異步任務函數
QFuture<QString> asyncTask(const QString &input) {return QtConcurrent::run([input]() {QThread::sleep(2);  // 模擬耗時操作return "Processed: " + input;});
}// 鏈式調用異步任務
void chainAsyncTasks() {// 第一個任務QFuture<QString> future1 = asyncTask("Task 1");// 創建監聽器QFutureWatcher<QString> *watcher1 = new QFutureWatcher<QString>(this);watcher1->setFuture(future1);// 連接完成信號connect(watcher1, &QFutureWatcher<QString>::finished, this, [this, watcher1]() {QString result1 = watcher1->result();qDebug() << "Result 1:" << result1;watcher1->deleteLater();// 啟動第二個任務QFuture<QString> future2 = asyncTask(result1);// 創建第二個監聽器QFutureWatcher<QString> *watcher2 = new QFutureWatcher<QString>(this);watcher2->setFuture(future2);// 連接第二個任務的完成信號connect(watcher2, &QFutureWatcher<QString>::finished, this, [watcher2]() {QString result2 = watcher2->result();qDebug() << "Result 2:" << result2;watcher2->deleteLater();});});
}

五、異步 UI 更新

1. 安全的 UI 更新方法
#include <QMainWindow>
#include <QPushButton>
#include <QThread>
#include <QDebug>class MainWindow : public QMainWindow {Q_OBJECT
public:explicit MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {m_button = new QPushButton("Start", this);setCentralWidget(m_button);connect(m_button, &QPushButton::clicked, this, &MainWindow::startTask);}private slots:void startTask() {// 創建工作線程QThread *thread = new QThread(this);// 創建工作者class Worker : public QObject {Q_OBJECTpublic:explicit Worker(QObject *parent = nullptr) : QObject(parent) {}signals:void progressUpdated(int value);public slots:void doWork() {for (int i = 0; i <= 100; ++i) {QThread::msleep(50);emit progressUpdated(i);}emit finished();}void finished() {thread()->quit();thread()->deleteLater();deleteLater();}};Worker *worker = new Worker();worker->moveToThread(thread);// 連接信號槽connect(thread, &QThread::started, worker, &Worker::doWork);connect(worker, &Worker::progressUpdated, this, &MainWindow::updateProgress);connect(worker, &Worker::finished, worker, &QObject::deleteLater);connect(thread, &QThread::finished, thread, &QObject::deleteLater);// 啟動線程thread->start();}void updateProgress(int value) {m_button->setText(QString("Progress: %1%").arg(value));}private:QPushButton *m_button;
};
2. 使用 Qt 的 invokeMethod
// 線程安全的 UI 更新
void updateUI(const QString &text) {// 使用 Qt::QueuedConnection 確保在主線程執行QMetaObject::invokeMethod(ui->label, "setText", Qt::QueuedConnection, Q_ARG(QString, text));
}// 在線程中調用
void Worker::run() {// 執行一些操作...// 更新 UIupdateUI("Operation completed");
}

六、異步編程最佳實踐

1. 避免阻塞事件循環
// 不良實踐:阻塞事件循環
void badPractice() {// 執行耗時操作,會阻塞 UIQThread::sleep(5);// 更新 UI,此時 UI 已凍結 5 秒ui->label->setText("Done");
}// 良好實踐:使用異步方式
void goodPractice() {// 在另一個線程執行耗時操作QtConcurrent::run([this]() {// 執行耗時操作QThread::sleep(5);// 更新 UI(在主線程執行)QMetaObject::invokeMethod(this, [this]() {ui->label->setText("Done");}, Qt::QueuedConnection);});
}
2. 合理管理異步資源
// 使用智能指針管理資源
void manageResources() {// 創建共享資源QSharedPointer<Resource> resource(new Resource());// 在異步任務中使用資源QtConcurrent::run([resource]() {// 使用資源resource->doSomething();});// 主線程可以繼續執行其他操作,資源會在所有引用消失后自動釋放
}

七、總結

Qt 提供了豐富的異步編程模式,能夠滿足不同場景下的需求。合理使用這些模式可以顯著提升應用的性能和響應性。本文介紹了 Qt 中主要的異步編程模式及其應用:

  1. 信號槽機制:Qt 核心的異步通信機制,線程安全且使用簡單
  2. QtConcurrent 框架:提供高級接口,簡化并行任務的實現
  3. 異步 I/O 操作:包括文件、網絡等 I/O 操作的異步實現
  4. 異步任務鏈與狀態機:用于組織復雜的異步工作流程
  5. 異步 UI 更新:安全更新 UI 的方法,避免界面凍結

在實際開發中,應根據具體場景選擇合適的異步模式,并遵循以下最佳實踐:

  • 避免阻塞事件循環,保持 UI 響應性
  • 合理管理異步資源,避免內存泄漏
  • 使用信號槽或 QMetaObject::invokeMethod 進行線程間通信
  • 考慮使用狀態機管理復雜的異步工作流程
  • 使用智能指針管理異步任務中的資源

通過這些技術和實踐,可以構建高效、穩定、響應迅速的 Qt 應用程序。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/90755.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/90755.shtml
英文地址,請注明出處:http://en.pswp.cn/web/90755.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

面試150 數字范圍按位與

思路 只要 left < right&#xff0c;說明兩者在某些低位上存在不同&#xff0c;為了找到它們的公共前綴&#xff08;高位相同部分&#xff09;&#xff0c;不斷將 left 和 right 同時右移&#xff08;即除以2&#xff09;&#xff0c;直到它們相等&#xff0c;記錄右移的次數…

數據庫HB OB mysql ck startrocks, ES存儲特點,以及應用場景

這些數據庫和存儲引擎主要有:HB(HBase)、OB(OceanBase)、MySQL、ClickHouse(CK)、StarRocks、Elasticsearch(ES),下面分別介紹它們的存儲特點以及典型應用場景。 1. HBase (HB) 存儲特點 分布式、面向列的NoSQL數據庫 采用HDFS存儲,數據以表、row key、列族、時間戳…

Java技術棧/面試題合集(17)-Git篇

場景 Java入門、進階、強化、擴展、知識體系完善等知識點學習、性能優化、源碼分析專欄分享: Java入門、進階、強化、擴展、知識體系完善等知識點學習、性能優化、源碼分析專欄分享_java高級進階-CSDN博客 通過對面試題進行系統的復習可以對Java體系的知識點進行查漏補缺。…

破局與重構:King’s LIMS 引領電子行業實驗室智能化轉型

在全球化高新技術競爭白熱化背景下&#xff0c;電子行業正經歷從規模導向擴張向質量效益躍升的戰略轉型。終端用戶對產品性能的極致化追求、行業質量合規標準的持續迭代升級&#xff0c;以及檢測數據的指數級增長&#xff0c;共同形成"需求牽引供給、供給創造需求"的…

暑期算法訓練.9

目錄 43 .力扣75 顏色分類 43.1 題目解析&#xff1a; 43.2 算法思路&#xff1a; 43.3 代碼演示&#xff1a; 43.4 總結反思&#xff1a; 44. 力扣 912 排序數組 44.1 題目解析&#xff1a; 44.2 算法思路&#xff1a; 44.3 代碼演示&#xff1a; ?編輯 44.4 總結反…

2.安裝CUDA詳細步驟(含安裝截圖)

2.安裝CUDA 第一步&#xff1a;安裝anaconda 注意&#xff1a;安裝CUDA之前需要安裝好anaconda&#xff0c;詳見安裝anaconda詳細步驟&#xff08;含安裝截圖&#xff09; 文章目錄2.安裝CUDA2.0 CUDA是什么&#xff0c;為什么要安裝它&#xff1f;2.1 驗證計算機是否安裝CUDA2…

Triton IR

Triton IR語法 Triton IR的語句遵從MLIR Dialect的語法定義規范&#xff0c;示例如下&#xff1a; %3 tt.splat %1 : i32 -> tensor<1024xi32> loc(#loc5) 其中&#xff1a; %0&#xff1a;右邊expression的結果值的名字&#xff08;Value的name&#xff09; tt…

掌握JavaScript函數封裝與作用域

JavaScript 基礎 - 第4天筆記理解封裝的意義&#xff0c;能夠通過函數的聲明實現邏輯的封裝&#xff0c;知道對象數據類型的特征&#xff0c;結合數學對象實現簡單計算功能。理解函數的封裝的特征掌握函數聲明的語法理解什么是函數的返回值知道并能使用常見的內置函數函數理解函…

Datawhale AI 夏令營—科大訊飛AI大賽(大模型技術)—讓大模型理解表格數據(列車信息表)

目錄 一、本次賽事目標&#xff1a;讓大模型理解表格數據&#xff08;列車信息表&#xff09; 二、分析賽題、對問題進行建模 賽事背景 賽題解讀 數據分析與探索 賽題要點與難點 解題思考過程 三、Baseline方案 Baseline概況 Baseline運行步驟 Baseline文件概況 Ba…

SSH連接失敗排查與解決教程: Connection refused

前言 當使用云服務器&#xff08;如阿里云、騰訊云、AWS 等&#xff09;時&#xff0c;嘗試在本地PC端使用圖形化工具如 FinalShell、XShell可能會遇到 SSH 連接失敗的問題。本文列舉 SSH 連接失敗的常見原因&#xff0c;并提供對應解決方案&#xff0c;幫助快速定位并解決問題…

性能優化:Vue 3 `v-memo` 指令詳解

v-memo 是 Vue 3 提供的一個性能優化工具&#xff0c;能幫助開發者緩存模板內容&#xff0c;減少不必要的渲染開銷。本文將介紹 v-memo 的引入版本、作用、使用方法和實現原理&#xff0c;并通過示例說明如何使用它。內容基于 Vue 3.5.18&#xff08;截至 2025 年 7 月的最新版…

標準庫開發和寄存器開發的區別

1.標準庫void GPIO_Toggle_INIT(void)//初始化GPIO {GPIO_InitTypeDef GPIO_InitStructure {0};//定義GPIO結構體RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);//使能GPIO時鐘GPIO_InitStructure.GPIO_Pin GPIO_Pin_2;//GPIO引腳選擇GPIO_InitStructure.GPIO_Mode …

在 WebSocket 中使用 @Autowired 時遇到空指針異常

背景&#xff1a;在websocket在有新的連接加入進來時&#xff0c;調用servier中的服務&#xff0c;使用 Autowired 注入的 Bean 竟然是 null&#xff01;這并非 Spring 的 Bug&#xff0c;而是對 WebSocket 生命周期管理理解不足導致的。了解這個問題&#xff0c;我們需要區分兩…

MGER實驗

一、實驗拓撲圖二、配置1.R5為ISP&#xff0c;只能進行IP地址配置&#xff0c;其所有地址均配為公有IP地址R1側為15.1.1.1&#xff0c;對應R5為15.1.1.2R2側為25.1.1.2&#xff0c;對應R5為25.1.1.1R3側為35.1.1.2&#xff0c;對應R5為35.1.1.1R4側為45.1.1.2&#xff0c;對應R…

基于 XGBoost 與 SHAP 的醫療自動化辦公與可視化系統(下)

— 登錄接口 — @app.post(“/token”) def login(form_data: OAuth2PasswordRequestForm = Depends()): user = fake_users_db.get(form_data.username) if not user or form_data.password != user[“password”]: raise HTTPException(status_code=400, detail=“用戶名或密…

python學智能算法(二十九)|SVM-拉格朗日函數求解中-KKT條件

引言 前序學習進程中&#xff0c;對拉格朗日函數執行了初步求導&#xff0c;并獲得了簡化后的拉格朗日函數極值計算式&#xff1a; L(w,b,α)∑i1mαi?12∑i,j1mαiαjyiyjxiTxjL(w,b,\alpha)\sum_{i1}^{m}\alpha_{i}-\frac{1}{2}\sum_{i,j1}^{m}\alpha_{i}\alpha_{j}y_{i}y_…

【AI論文】MiroMind-M1:通過情境感知多階段策略優化實現數學推理的開源新進展

摘要&#xff1a;近期&#xff0c;大型語言模型已從流暢的文本生成發展至能在多個領域進行高級推理&#xff0c;由此催生了推理語言模型&#xff08;RLMs&#xff09;。在眾多領域中&#xff0c;數學推理堪稱代表性基準&#xff0c;因為它需要精確的多步驟邏輯與抽象推理能力&a…

《使用Qt Quick從零構建AI螺絲瑕疵檢測系統》——6. 傳統算法實戰:用OpenCV測量螺絲尺寸

目錄一、概述1.1 背景介紹&#xff1a;從“看見”到“看懂”1.2 學習目標二、圖像預處理&#xff1a;讓目標更突出三、輪廓發現與尺寸測量四、總結與展望一、概述 1.1 背景介紹&#xff1a;從“看見”到“看懂” 在上一篇文章中&#xff0c;我們成功地為應用程序安裝了“眼睛…

《人性的弱點》重構【01】

手上有本《人性的弱點》&#xff08;韓文橋 譯&#xff0c;浙江文藝出版社&#xff0c;2017.1出版&#xff09;&#xff0c;前些年買的&#xff0c;近期翻出來看看。這門書雖成書于80多年前&#xff0c;但卡耐基對人性洞察之深刻&#xff0c;時至今日&#xff0c;并未覺得過時。…

k8s開啟審計日志

k8s默認是關閉審計功能的&#xff0c;想看的話需要到apiserver的pod中才可以。 開啟此功能是為了進行k8s審計日志的收集&#xff0c;方便我們查看k8s中用戶的各自操作。 開啟此功能之前&#xff0c;我們要先創建個審計策略文件audit-policy.yaml 例如以下的測驗文件 apiVersion…