【QT】多線程相關教程

一、核心概念與 Qt 線程模型

1.線程與進程的區別:
線程是程序執行的最小單元,進程是資源分配的最小單元,線程共享進程的內存空間(堆,全局變量等),而進程擁有獨立的內存空間。Qt線程只要關注同一進程內的并發。

2.為什么使用多線程
當程序中有多個耗時的操作時候,為了提高性能,防止GUI線程阻塞,可以處理耗時操作

3.QThread 類的相關接口
start():啟動線程,調用run方法
run():線程的入口點,子類化 QThread 并重寫此方法是一種使用線程的方式(傳統方式)。線程在此函數中執行。
quit() / exit(int returnCode): 請求線程退出事件循環(如果正在運行)。
wait([unsigned long time = ULONG_MAX]): 阻塞調用線程,直到目標線程結束執行或超時。
isRunning() / isFinished(): 查詢線程狀態。
finished 信號:線程執行完畢(run() 返回)時發射。重要: 連接此信號進行資源清理(如 deleteLater)。
started 信號:線程啟動后(run() 執行前)發射。

4.事件循環 (Event Loop)
核心概念: 每個線程都可以擁有自己的事件循環(由 QEventLoop 管理)。主線程(GUI 線程)默認運行事件循環。
作用: 處理事件(如定時器事件、網絡事件、投遞的事件、隊列連接的信號槽調用)。
QThread::exec(): 進入事件循環(在 run() 方法中調用)。
Worker 對象 + moveToThread + 事件循環模式: 這是現代 Qt 多線程編程的主流模式。對象被移動到新線程后,它的槽函數將在新線程的事件循環中被調用。

6.信號與槽 (Signals & Slots) 的連接類型
Qt 的信號槽機制是線程安全的。
連接類型 (Connection Type) 決定槽函數在哪個線程執行:
Qt::AutoConnection (默認): 如果發送者和接收者在同一線程,行為同 DirectConnection;否則,行為同 QueuedConnection。
Qt::DirectConnection: 槽函數在發送者所在線程中立即執行(就像直接函數調用)。
Qt::QueuedConnection: 槽函數的調用被轉換為一個事件,放入接收者所在線程的事件隊列。接收者線程的事件循環稍后會從隊列中取出并執行該槽函數。這是跨線程通信最安全、最常用的方式!
Qt::BlockingQueuedConnection: 類似 QueuedConnection,但發送者線程會阻塞,直到接收者線程的槽函數執行完畢。慎用!容易死鎖。 確保接收者線程能及時處理事件。
Qt::UniqueConnection: 可以與上述類型組合使用 (AutoConnection | UniqueConnection),確保相同的信號和槽之間只有一個連接。

跨線程信號槽參數傳遞: 參數類型必須是 Qt 元對象系統已知的類型( 使用qRegisterMetaType() 注冊自定義類型)。對于 QueuedConnection 和 BlockingQueuedConnection,參數會被復制傳遞。

二、創建和管理線程

1.使用 QThread 的兩種主要模式:
使用 QThread 的兩種主要模式:
1.1 子類化 QThread (傳統方式):
(1)繼承 QThread。
(2)重寫 run() 方法,將需要在線程中執行的代碼放入其中。
(3)創建子類實例,調用 start()。
局限: run() 是唯一入口,難以處理多個任務或利用事件循環。對象本身(this)仍留在原線程(通常是主線程)。
在QThread子類的構造函數中創建的對象,其線程親和性是創建該QThread對象的線程(通常是主線程),而不是新線程。這會導致在該對象上使用定時器、信號槽等出現問題。正確的方式是在run()函數中創建這些對象,這樣它們的線程親和性才是新線程。但這樣又使得對象創建在run()函數內部,難以從外部管理。線程結束時要清理run()中創建的對象,需要自己管理,容易出錯。

1.2 Worker 對象 + moveToThread (推薦方式):
(1)創建一個普通的 QObject 子類(Worker 對象),它包含需要通過槽函數執行的任務。
(2)創建一個 QThread 實例。
(3)創建 Worker 對象實例(此時它在創建者線程,通常是主線程)。
(4)調用 workerObject->moveToThread(workerThread)。關鍵步驟!
(5)連接 Worker 對象的信號和槽(通常使用 QueuedConnection,但 AutoConnection 在 moveToThread 后通常也能正確處理)。
(6)啟動線程 workerThread->start()。這會啟動線程的事件循環。
(7)通過信號觸發 Worker 對象的槽函數執行任務。任務將在新線程中執行。
(8).請求線程退出:workerThread->quit() 或 workerThread->requestInterruption()(更安全)。
(9)連接 workerThread->finished() 信號到 workerThread->deleteLater() 和 workerObject->deleteLater() 進行自動清理。

//主線程中創建:QThread 實例和worker 對象
Worker* worker = new Worker(); 
QThread* thread = new QThread(this);
worker->moveToThread(thread );
//現在worker屬于新線程
thread->start(); // 內部調用exec()啟動事件循環
//通過信號槽提交任務
QObject::connect(this, &Controller::startTask, worker, &Worker::doWork);
// 線程結束時自動清理,不然手動在析構函數中delect worker也可以
connect(thread, &QThread::finished, worker, &QObject::deleteLater);
/*因為worker有指定父對象所有這兒不用刪除了
connect(thread, &QThread::finished, thread, &QObject::deleteLater); */// 在子線程執行耗時操作    
void Worker::doWork() {............
}      

在這里插入圖片描述
關鍵原則:誰創建誰刪除,跨線程對象使用 deleteLater

在這里插入圖片描述

新舊方式的對比
在這里插入圖片描述

在 Worker 對象中不要做:
創建 QWidget 或其子類(GUI 對象必須在 GUI 線程創建)。
直接操作 GUI(通過信號通知 GUI 線程更新)。
優點: 更符合 Qt 對象模型,可以利用事件循環處理多個任務(定時器、網絡等),更靈活,資源管理更清晰。

2.線程池 (QThreadPool 和 QtConcurrent)
QThreadPool: 管理一組可重用的線程。用于執行 QRunnable 任務。
QRunnable: 定義需要執行的任務(重寫 run() 方法)。
QThreadPool::globalInstance(): 獲取全局線程池實例。
QThreadPool::start(QRunnable *task, int priority = 0): 提交任務到線程池。
優點: 避免頻繁創建銷毀線程的開銷,控制最大并發數。

QtConcurrent 命名空間: 提供高級 API 簡化并行計算,底層通常使用 QThreadPool。
run(Function function, …): 在單獨線程中運行函數。
map(), mapped(), filtered(), reduce() 等:對容器進行并行操作。
QFuture, QFutureWatcher: 用于監控異步計算的結果和狀態。
優點: 代碼簡潔,易于使用,適合數據并行任務。

三、線程同步與通信

1.互斥鎖 QMutex 保護共享數據訪問

2.讀寫鎖 (QReadWriteLock, QReadLocker, QWriteLocker) 優化“讀多寫少”場景。

3.信號量(QSemaphore) 控制對多個相同資源的訪問。

4.條件變量
允許線程在特定條件不滿足時睡眠等待,并在條件可能改變時被其他線程喚醒。

wait(QMutex *lockedMutex): 原子操作: 釋放 lockedMutex 并阻塞等待。被喚醒后,在返回前會重新獲取 lockedMutex。

wakeOne(): 喚醒一個等待的線程(任意)。

wakeAll(): 喚醒所有等待的線程。

經典模式: 生產者-消費者。

5.原子操作
對基本數據類型(整數、指針)提供無鎖的原子操作(讀、寫、加減、比較交換等)。

輕量級, 適用于簡單的計數器、標志位等場景。不能替代鎖保護復雜操作或多變量。
6.跨線程通信的首選:信號與槽
這是 Qt 中最安全、最便捷的跨線程通信機制。利用事件循環傳遞消息

四、線程安全與最佳實踐

1.GUI 線程規則 (黃金法則):
所有用戶界面操作(創建、訪問、更新 QWidget 及其子類)都必須在主線程(GUI 線程)中進行。

子線程需要更新 UI 時,必須通過信號槽(QueuedConnection)通知主線程進行更新。
2.資源管理
對象樹與所有權: Qt 的父子關系管理在跨線程時不適用。父對象和子對象必須在同一線程。

動態對象創建與銷毀:

在哪個線程創建對象,該對象通常就“屬于”那個線程。

使用 moveToThread() 改變所有權。

安全刪除: 使用 obj->deleteLater()。該方法會將刪除請求放入對象所在線程的事件隊列,由事件循環安全地執行刪除操作。這是跨線程刪除對象的正確方式! 特別是在連接 QThread::finished() 信號時使用。

3.避免死鎖
遵循鎖的固定順序。

最小化臨界區(持鎖時間)。

謹慎使用嵌套鎖。

優先使用 RAII 鎖管理 (QMutexLocker 等)。

避免在持鎖時等待另一個線程的信號(容易死鎖),或使用帶超時的等待。
4.退出
請求退出,而非強制終止 (terminate() 非常危險,可能導致資源泄露、狀態不一致,應避免使用)。

使用 QThread::requestInterruption() 設置中斷請求標志。

在 Worker 對象的耗時操作中定期檢查 QThread::isInterruptionRequested(),并在檢測到時提前退出。

調用 quit() 或 exit() 請求線程退出事件循環。

使用 wait()(可選,需設置合理超時)確保線程結束。

利用 finished() 信號進行清理 (deleteLater)。
5.異常處理
線程中的異常不會傳播到創建該線程的線程(如主線程)。

必須在 run() 或 Worker 對象的槽函數內部捕獲并處理所有可能的異常,否則會導致線程崩潰(整個進程通常不會退出,但該線程的工作停止)。
6.性能考量
線程創建銷毀有開銷,優先考慮線程池 (QThreadPool, QtConcurrent)。

同步原語(鎖)有開銷,盡量減少競爭(鎖粒度、讀寫鎖、原子操作)。

跨線程通信(信號槽 QueuedConnection)有事件投遞和復制的開銷。

平衡線程數量和任務粒度。

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

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

相關文章

VS 版本更新git安全保護問題的解決

問題:我可能移動了一個VS C# 項目,然后,發現里面的git版本檢測不能用了 正在打開存儲庫: X:\Prj_C#\3D fatal: detected dubious ownership in repository at X:/Prj_C#/3DSnapCatch X:/Prj_C#/3D is owned by:S-1-5-32-544 but the current …

Git常用命令一覽

Git 是基于 Linux內核開發的版本控制工具。與常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本庫的方式,不必服務器端軟件支持(ps:這得分是用什么樣的服務端,使用http協議或者git協議等不太一樣。并且在…

基于 JSON 文件定位圖片缺陷點并保存

基于JSON的圖片缺陷處理流程 ├── 1. 輸入檢查 │ ├── 驗證圖片文件是否存在 │ └── 驗證JSON文件是否存在 │ ├── 2. 數據加載 │ ├── 打開并加載圖片 │ └── 讀取并解析JSON文件 │ ├── 3. 缺陷信息提取 │ ├── 檢查JSON中是否存在shapes字…

Redis基礎學習(五大值數據類型的常用操作命令)

目錄 一、Redis基本知識與Redis鍵(key)常用操作命令。 二、Redis的五大值的數據類型。(value) 三、Redis關于鍵(key)的值常用操作指令表格統計。 (1)字符串(String&#…

Ubuntu——辦公軟件 LibreOffice 安裝與使用指南

十四、LibreOffice 安裝與使用1、核心組件組件????圖標????對應MS Office????核心功能定位????Writer??📝Word專業文檔處理與排版??Calc??📊Excel數據計算與分析??Impress??🎬PowerPoint演示文稿制作??Draw??&…

Securecrt丟失tab以及終端重新配色

今天在使用 Securecrt 的時候,發現 Securecrt 的 tab 標簽消失不見了,仔細回想起來,應該是上一次誤按了 alt enter 最大化,然后導致配置丟失的問題 還有表現就是菜單中的 Session Tabs 無論勾選還是不勾選都沒有任何變化&#xf…

frp搭建內網穿透教程

frp搭建內網穿透教程 步驟1:準備工作 公網服務器:需要一臺具有公網IP的服務器作為中轉服務器,安裝frp服務器端(frps)。內網設備:需要暴露服務的內網設備,安裝frp客戶端(frpc&#xf…

【JavaEE進階】圖書管理系統(未完待續)

目錄 用戶登錄 添加圖書 圖書列表 修改圖書 刪除圖書 批量刪除 攔截器 🍃前言 什么是攔截器? 攔截器的基本使用 自定義攔截器 注冊配置攔截器 攔截路徑 攔截器執行流程 項目實現統一攔截 定義攔截器 注冊配置攔截器 前?圖書管理系統, 咱們只完成了??登錄和圖書列…

基于同花順API的熊市與牛市識別模型開發及因子分析

基于同花順API的熊市與牛市識別模型開發及因子分析 1. 引言 1.1 研究背景與意義 金融市場中的牛市與熊市識別一直是投資者和研究人員關注的重點問題。牛市(Bull Market)通常指價格持續上漲的市場環境,投資者信心充足,交易活躍;而熊市(Bear Market)則指價格持續下跌的市場…

AMD 銳龍 AI MAX+ 395 處理器與端側 AI 部署的行業實踐

2025 年 7 月 10 日,AMD 在深圳召開 Mini AI 工作站行業解決方案峰會,正式發布基于銳龍 AI MAX 395 處理器的端側 AI 部署方案,與 200 余家生態伙伴共同探討 AI 技術在千行百業的落地路徑。這一硬件平臺通過異構計算架構與開放生態設計&#…

期權盤位是什么意思?

本文主要介紹期權盤位是什么意思?“期權盤位”并非金融交易中的標準術語,可能是口語化表達或對某些概念的簡化描述。期權盤位是什么意思?1. 期權盤口的“價位”(買賣報價位置)在期權交易中,“盤口”通常指實…

【Trea】Trea國內版|國際版|海外版下載|Mac版|Windows版|Linux下載配置教程

【Trea】Trea國內版|國際版|海外版下載|Mac版|Windows版下載配置教程 本文適用讀者: 想要第一次安裝 Trea需要在 Windows 或 macOS 上完成環境配置想深入了解 Doubao、DeepSeek、ChatGPT、Claude 等模型在 Trea 中的接…

MyBatis實現分頁查詢-蒼穹外賣筆記

首先分頁查詢的原理是SQL的limit關鍵字。LIMIT 子句用于限制 SQL 查詢返回的記錄數。它接受一個或兩個整數參數,第一個參數表示偏移量,第二個參數表示返回的最大記錄數。我們完全可以使用前端傳給我們的page,pageSize,自己去計算limit的參數,…

系統性能評估方法深度解析:從經典到現代

評估本質:系統性能評估是通過量化分析衡量計算機系統在特定工作負載下的表現能力,核心目標是建立可比較的性能基準,為系統設計、選型和優化提供科學依據。一、評估方法分類體系 #mermaid-svg-0ceD4AA2KDwzwtb6 {font-family:"trebuchet …

WebSocket實現多人實時在線聊天

最近公司在做一個婚戀app&#xff0c;需要增加一個功能&#xff0c;實現多人實時在線聊天。基于WebSocket在Springboot中的使用&#xff0c;前端使用vue開發。 一&#xff1a;后端 1. 引入 websocket 的 maven 依賴 <dependency><groupId>org.springframework.bo…

學習筆記隨記-FPGA/硬件加速

一、FPGA&#xff1a;Field Programmable Gate Array 現場可編程門陣列 可編程輸入/輸出單元、基本可編程邏輯單元、嵌入式塊RAM、豐富的布線資源、底層嵌入功能單元和內嵌專用硬核。 可編程輸入/輸出單元&#xff08;I/O&#xff09;單元 輸入/輸出&#xff08;Input/Ouput&…

docker宿主機修改ip后起不來問題解決

確保容器已經連接到了正確的網絡。如果沒有&#xff0c;你可以使用以下命令將容器連接到網絡&#xff1a; 1、停止docker網絡 ifconfig docker0 down1. 停止 Docker 服務 sudo systemctl stop docker2. 刪除 docker0 接口 sudo ip link delete docker03、刪除舊的網橋 docker n…

G1 垃圾回收算法詳解

目錄 簡介 G1 GC 的設計目標 內存結構 回收過程 初始標記&#xff08;Initial Mark&#xff09;并發標記&#xff08;Concurrent Mark&#xff09;最終標記&#xff08;Final Mark / Remark&#xff09;篩選回收&#xff08;Cleanup / Evacuation&#xff09; 混合回收&…

JavaEE多線程——鎖策略 CAS synchronized優化

目錄前言1.鎖策略1.1 樂觀鎖和悲觀鎖1.2 重量級鎖和輕量級鎖1.3 掛起等待鎖和自旋鎖1.4 公平鎖和非公平鎖1.5 可重入鎖和不可重入鎖1.6 讀寫鎖2.CAS2.1 CAS的應用2.2 CAS的ABA問題3.synchronized優化3.1鎖升級3.2鎖消除3.3鎖粗化總結前言 本篇文章主要介紹多線程中鎖策略、CAS…

Windows符號鏈接解決vscode和pycharm占用C盤空間太大的問題

Windows符號鏈接解決vscode和pycharm占用C盤空間太大的問題 參考文章&#xff1a;Windows符號鏈接 1、找到vscode和pycharm在C盤的緩存文件夾。 C:\Users\用戶名\AppData\Roaming\Code C:\Users\用戶名\.vscode\extensionsC:\Users\用戶名\AppData\Local\JetBrains C:\Users…