QT之QWaitCondition降低cpu占用率,從忙等待到高效同步

在多線程編程中,線程間的同步是一個核心問題。在處理線程等待時,經常會寫出高CPU占用率的代碼,其中最典型的就是使用忙等待(busy waiting)。本文將詳細介紹如何使用Qt框

架中的QWaitCondition類來優雅地解決這一問題,有效降低CPU占用率。

一、占用率高分析

如上圖,這種常見寫法確實會導致 CPU 常駐一定百分比左右,原因是:

  1. 線程永遠不會退出??while(true) 保持運行,即使沒有任務也會醒來一次;

  2. msleep(500) 只是讓線程休眠 500ms?休眠期間 CPU 占用是 0,但醒來后要判斷條件、調用函數 → 系統監視器會統計為少量 CPU 占用,就算循環里幾乎什么都不做,CPU 也要處理線程切換;

總結,采用msleep其是一種實時性CPU 占用 的權衡,msleep 越短線程被喚醒得越頻繁,響應快,CPU 占用越高(比如 msleep(1) 就幾乎是“忙等”,會吃掉比較多 CPU)。而 msleep 越長 CPU 占用低,但可能延遲處理任務(比如 500ms 意味著最壞情況延遲半秒)。這種寫法的問題顯而易見:

  • CPU持續進行無效的檢查循環
  • 即使使用msleep,仍然會產生不必要的上下文切換
  • 響應延遲不可控
  • 資源浪費不可避免

二、QWaitCondition:優雅的解決方案

QWaitCondition是Qt提供的條件變量實現,它允許線程在特定條件滿足之前進入休眠狀態,從而避免忙等待。

2.1、基本工作原理

QWaitCondition的核心機制:

  1. wait() - 線程釋放互斥鎖并進入休眠狀態
  2. wakeOne()/wakeAll() - 喚醒一個或所有等待的線程
  3. 被喚醒的線程重新獲取互斥鎖并繼續執行

2.2、wakeOne喚醒一個

下方演示一個最小可運行的 Qt C++ QWaitCondition 生產者–消費者 Demo
這個程序有兩個線程:

  • Producer:每隔一秒產生一個任務。

  • Consumer:只有收到任務才會被喚醒并處理,空閑時 CPU 完全是 0%

#include <QCoreApplication>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QQueue>
#include <QDebug>// 全局隊列 & 條件變量
QMutex mutex;
QWaitCondition cond;
QQueue<int> queue;// 消費者線程
class Consumer : public QThread {
protected:void run() override {while (true) {mutex.lock();// 如果隊列為空,就等待while (queue.isEmpty()) {cond.wait(&mutex); // 會自動解鎖并掛起,直到被喚醒}int task = queue.dequeue();mutex.unlock();qDebug() << "Consumer: processing task" << task;QThread::msleep(200); // 模擬耗時任務}}
};// 生產者線程
class Producer : public QThread {
protected:void run() override {int counter = 0;while (true) {QThread::sleep(1); // 每秒生產一個任務mutex.lock();queue.enqueue(++counter);qDebug() << "Producer: produced task" << counter;cond.wakeOne(); // 喚醒一個等待的消費者mutex.unlock();}}
};int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);Consumer consumer;Producer producer;consumer.start();producer.start();return a.exec();
}

Producer: produced task 1
Consumer: processing task 1
Producer: produced task 2
Consumer: processing task 2
...

此時觀察任務管理器 / top,可以看到 CPU 占用在 沒有任務時幾乎 0%,有任務時才會短暫占用

2.3、wakeAll喚醒所有

下面創建 3 個消費者線程,同時等待任務隊列里的數據,同時用 QMutexLocker 可以避免忘記 unlock() 導致死鎖;

#include <QCoreApplication>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QQueue>
#include <QDebug>// 全局任務隊列 & 條件變量
QMutex mutex;
QWaitCondition cond;
QQueue<int> queue;// 消費者線程
class Consumer : public QThread {
public:Consumer(int id) : m_id(id) {}protected:void run() override {while (true) {QMutexLocker locker(&mutex);// 如果隊列為空,就等待while (queue.isEmpty()) {cond.wait(&mutex);  // 注意:這里必須傳裸 QMutex 指針}int task = queue.dequeue();locker.unlock();  // 手動提前解鎖,讓其他線程能進入qDebug() << "Consumer" << m_id << "processing task" << task;QThread::msleep(300); // 模擬耗時任務}}private:int m_id;
};// 生產者線程
class Producer : public QThread {
protected:void run() override {int counter = 0;while (true) {QThread::sleep(1); // 每秒生產一個任務{QMutexLocker locker(&mutex);queue.enqueue(++counter);qDebug() << "Producer produced task" << counter;//cond.wakeOne(); // 喚醒一個等待的消費者cond.wakeAll();//所有消費者都被喚醒} // locker 作用域結束時自動解鎖}}
};int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);// 啟動 3 個消費者線程Consumer c1(1), c2(2), c3(3);Producer producer;c1.start();c2.start();c3.start();producer.start();return a.exec();
}
  • cond.wakeOne() → 每次只喚醒 一個等待的消費者(其他仍然掛起)。

  • cond.wakeAll() → 會喚醒 所有等待的消費者(此時誰先拿到鎖,誰先消費)

QMutexLocker locker(&mutex); → 構造時自動加鎖,析構時自動解鎖。

cond.wait() 推薦傳裸 QMutex*,不要傳 QMutexLocker;

在消費者里?locker.unlock() 提前解鎖,避免持鎖期間去做耗時任務(否則會阻塞其他線程取任務);

cond.wait(&mutex, 3000); // 也可以設置3秒超時防止死鎖

三、總結

QWaitCondition是Qt中處理線程同步的強大工具,它通過避免忙等待顯著降低了CPU占用率。關鍵要點:

  1. 使用條件變量替代忙等待循環
  2. 結合互斥鎖確保線程安全
  3. 合理使用超時機制避免無限阻塞
  4. 選擇適當的喚醒策略優化性能

通過正確使用QWaitCondition,可以構建出既高效又穩定的多線程應用程序,在保證功能正確性的同時最小化系統資源消耗,高質量的多線程代碼不僅要功能正確,還要在性能和資源使用上做到優雅高效,QWaitCondition正是達到這一目標的利器。

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

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

相關文章

pcl求平面點云的邊界凸包點

基本流程1&#xff0c;讀入點云&#xff0c;并去除無效點2&#xff0c;擬合平面3&#xff0c;去除離平面距離較遠的點4&#xff0c;對點云進行平面投影5&#xff0c;進行convex_hull運算初學者&#xff0c;暫時不知道能用來干嘛。練手還是非常不錯的&#xff01;#define _CRT_S…

Windows系統上使用GIT

首先破除一下畏懼心理&#xff1a;在Windows上使用git和在linux系統中的使用方法是一樣的&#xff0c;只是安裝方式沒那么便捷&#xff0c;畢竟linux中安裝git只需要一行命令 GIT下載地址 如果你的電腦的CPU是64位的&#xff0c;就點擊&#xff1a; Git-2.50.1-64-bit.exe 如果…

《設計模式之禪》筆記摘錄 - 17.模板方法模式

模板方法模式的定義模板方法模式(Template Method Pattern)是如此簡單&#xff0c;以致讓你感覺你已經能夠掌握其精髓了。其定義如下&#xff1a;Define the skeleton of an algorithm in an operation, deferring some steps to subclasses.Template Method lets subclasses r…

SpreadJS 協同服務器 MongoDB 數據庫適配支持

為了支持 SpreadJS 協同編輯場景&#xff0c;協同服務器需要持久化存儲文檔、操作、快照及里程碑數據。本文介紹了 MongoDB 數據庫適配器的實現方法&#xff0c;包括集合初始化、適配器接口實現以及里程碑存儲支持。 一、MongoDB 集合初始化 協同編輯服務需要以下集合&#x…

Ubuntu 主機名:精通配置與管理

主機名&#xff08;hostname&#xff09;是Linux系統中用于標識網絡上特定設備的名稱&#xff0c;它在網絡通信、服務配置&#xff08;如 Kubernetes 集群、數據庫&#xff09;以及日志記錄中扮演著至關重要的角色。對于初學者來說&#xff0c;配置主機名似乎很簡單&#xff0c…

C/C++ 協程:Stackful 手動控制的工程必然性

&#x1f680; C/C 協程&#xff1a;Stackful 手動控制的工程必然性 引用&#xff1a; C/C 如何正確的切換協同程序&#xff1f;&#xff08;基于協程的并行架構&#xff09; #mermaid-svg-SXgplRf3WRYc8A7l {font-family:"trebuchet ms",verdana,arial,sans-serif;…

新手向:使用STM32通過RS485通信接口控制步進電機

新手向&#xff1a;使用STM32通過RS485通信接口控制步進電機 準備工作 本文使用的STM32芯片是STM32F407ZGTx&#xff0c;使用的電機是57步進電機&#xff0c;驅動器是用的是時代超群的RS485總線一體化步進電機驅動器&#xff08;42 型&#xff1a;ZD-M42P-485&#xff09;。使…

設計模式筆記_行為型_命令模式

1.命令模式介紹命令模式&#xff08;Command Pattern&#xff09;是一種行為設計模式&#xff0c;它將請求或操作封裝為對象&#xff0c;使得可以用不同的請求對客戶端進行參數化。命令模式的核心思想是將方法調用、請求或操作封裝到一個獨立的命令對象中&#xff0c;從而使得客…

詳解MySQL中的多表查詢:多表查詢分類講解、七種JOIN操作的實現

精選專欄鏈接 &#x1f517; MySQL技術筆記專欄Redis技術筆記專欄大模型搭建專欄Python學習筆記專欄深度學習算法專欄 歡迎訂閱&#xff0c;點贊&#xff0b;關注&#xff0c;每日精進1%&#xff0c;與百萬開發者共攀技術珠峰 更多內容持續更新中&#xff01;希望能給大家帶來…

vue3+elemeent-plus, el-tooltip的樣式修改不生效

修改后的樣式&#xff0c;直接貼圖&#xff0c;經過刪除出現懸浮1、在書寫代碼的時候切記effect“light”&#xff0c;如果你需要的是深色的樣式:disabled"!multiple" 是否禁用<el-tooltip effect"light" placement"top" content"請先選…

網頁作品驚艷亮相!這個浪浪山小妖怪網站太治愈了!

大家好呀&#xff01;今天要給大家分享一個超級治愈的網頁作品——浪浪山小妖怪主題網站&#xff01;這個純原生開發的項目不僅顏值在線&#xff0c;功能也很能打哦&#xff5e;至于靈感來源的話&#xff0c;要從一部動畫說起。最近迷上了治愈系動畫&#xff0c;就想做一個溫暖…

搭建最新--若依分布式spring cloudv3.6.6 前后端分離項目--步驟與記錄常見的坑

首先 什么拉取代碼&#xff0c;安裝數據庫&#xff0c;安裝redis&#xff0c;安裝jdk這些我就不說了 導入數據庫 &#xff1a;數據庫是分庫表的 &#xff0c;不要建錯了 【一定要注意&#xff0c;不然nacos讀取不到配置文件】這個是給nacos用的這個是給項目配置或項目用的2. 服…

分布式唯一 ID 生成方案

在復雜分布式系統中&#xff0c;往往需要對大量的數據和消息進行唯一標識。如在美團點評的金融、支付、餐飲、酒店、貓眼電影等產品的系統中&#xff0c;數據日漸增長&#xff0c;對數據分庫分表后需要有一個唯一 ID 來標識一條數據或消息&#xff0c;數據庫的自增 ID 顯然不能…

飛算JavaAI賦能高吞吐服務器模擬:從0到百萬級QPS的“流量洪峰”征服之旅

引言&#xff1a;當“流量洪峰”來襲&#xff0c;如何用低代碼馴服高并發&#xff1f; 在數字化時代&#xff0c;從電商平臺的“雙11”大促到社交網絡的突發熱點事件&#xff0c;再到金融系統的實時交易高峰&#xff0c;服務器時刻面臨著**高吞吐量&#xff08;High Throughput…

C#數據訪問幫助類

一.中文注釋using System; using System.Data; using System.Xml; using System.Data.SqlClient; using System.Collections;namespace Microsoft.ApplicationBlocks.Data.Ch {/// <summary>/// SqlServer數據訪問幫助類/// </summary>public sealed class SqlHelp…

B站 韓順平 筆記 (Day 21)

目錄 1&#xff08;面向對象高級部分練習題&#xff09; 1.1&#xff08;題1&#xff09; 1.2&#xff08;題2&#xff09; 1.3&#xff08;題3&#xff09; Vehicles接口類&#xff1a; Horse類&#xff1a; Boat類&#xff1a; Plane類&#xff1a; VehiclesFactory…

Linux(十四)——進程管理和計劃任務管理

文章目錄前言一、程序與進程的關系1.1 程序與進程的定義1.2 父進程與子進程二、查看進程信息2.1 ps 命令&#xff08;重點&#xff09;2.2 動態查看進程信息top命令&#xff08;重點&#xff09;2.3 pgrep命令查詢進程信息2.4 pstree命令以樹形結構列出進程信息三、進程的啟動方…

太陽光模擬器在無人機老化測試中的應用

在無人機技術飛速發展的當下&#xff0c;其戶外作業環境復雜多變&#xff0c;長期暴露在陽光照射下&#xff0c;部件老化問題日益凸顯&#xff0c;嚴重影響無人機的性能與壽命。紫創測控Luminbox專注于太陽光模擬器技術創新與精密光學測試系統開發&#xff0c;其涵蓋的 LED、鹵…

網絡原理-TCP_IP

1.UDP&#xff08;即用戶數據報協議&#xff09;UDP是一種無連接的傳輸層協議&#xff0c;提供簡單的、不可靠的數據傳輸服務。它不保證數據包的順序、可靠性或重復性&#xff0c;但具有低延遲和高效率的特點。UDP協議段格式16位UDP?度,表?整個數據報(UDP?部UDP數據)的最??…

GitHub Actions YAML命令使用指南

version: 2 updates:- package-ecosystem: "github-actions"directory: "/"schedule:interval: "weekly"這段代碼是 Dependabot 的配置文件&#xff08;通常放在 .github/dependabot.yml 中&#xff09;&#xff0c;它的作用是 自動化管理 GitHu…