Qt/C++面試【速通筆記六】—Qt 中的線程同步

在多線程編程中,多個線程同時訪問共享資源時,可能會出現數據不一致或者錯誤的情況。這時,我們需要線程同步機制來保證程序的正確性。Qt 提供了多種線程同步方式,每種方式適用于不同的場景。


1. 互斥鎖(QMutex)

QMutex 是最常用的線程同步機制之一,它用于保護共享資源,確保同一時刻只有一個線程可以訪問資源。互斥鎖(Mutex)也叫排他鎖,是一種獨占的鎖。

代碼示例:
#include <QMutex>
#include <QThread>
#include <QDebug>QMutex mutex;  // 聲明一個互斥鎖
int sharedData = 0;  // 共享數據// 安全地遞增共享數據
void safeIncrement() {mutex.lock();  // 加鎖,確保只有一個線程能訪問共享數據sharedData++;  // 對共享數據進行操作mutex.unlock();  // 解鎖,允許其他線程訪問
}class MyThread : public QThread {
public:void run() override {for (int i = 0; i < 1000; ++i) {safeIncrement();  // 調用安全遞增函數}}
};int main() {MyThread thread1, thread2;  // 創建兩個線程thread1.start();  // 啟動線程1thread2.start();  // 啟動線程2thread1.wait();  // 等待線程1結束thread2.wait();  // 等待線程2結束qDebug() << "最終共享數據的值:" << sharedData;  // 輸出共享數據的最終值
}

注釋:

  • 在多線程環境下,sharedData 是多個線程共享的資源。我們通過 QMutex 進行加鎖和解鎖操作,確保同一時刻只有一個線程可以訪問和修改 sharedData

2. 讀寫鎖(QReadWriteLock)

QReadWriteLock 是一種更細粒度的鎖機制。它允許多個線程同時讀取數據,但在寫數據時,必須獨占鎖。這樣,在讀取時多個線程可以并發執行,但寫入時會阻塞其他線程的讀取和寫入。

代碼示例:
#include <QReadWriteLock>
#include <QThread>
#include <QDebug>QReadWriteLock lock;  // 聲明讀寫鎖
int sharedData = 0;  // 共享數據// 讀取共享數據
void readData() {lock.lockForRead();  // 加讀鎖,允許多個線程并發讀取qDebug() << "讀取共享數據:" << sharedData;lock.unlock();  // 解鎖
}// 寫入共享數據
void writeData(int value) {lock.lockForWrite();  // 加寫鎖,獨占鎖sharedData = value;qDebug() << "寫入共享數據:" << sharedData;lock.unlock();  // 解鎖
}class MyThread : public QThread {
public:void run() override {for (int i = 0; i < 5; ++i) {readData();  // 讀取共享數據writeData(i);  // 寫入共享數據}}
};int main() {MyThread thread1, thread2;  // 創建兩個線程thread1.start();  // 啟動線程1thread2.start();  // 啟動線程2thread1.wait();  // 等待線程1結束thread2.wait();  // 等待線程2結束
}

注釋:

  • QReadWriteLock 的好處是允許多個線程同時讀取數據,但寫操作時會阻塞其他線程的讀取和寫入。適合數據不經常改變但需要頻繁讀取的場景。

3. 事件和信號槽機制(QEvent, QSignalEmitter)

Qt 的信號和槽機制是非常強大的一種線程間通信方式。它通過事件機制,讓一個線程發出信號,另一個線程接收并響應這個信號,避免了顯式的鎖操作。信號槽機制通過 Qt 自己的事件循環來實現線程安全的通信。

代碼示例:
worker.h — Worker 類定義
#ifndef WORKER_H
#define WORKER_H#include <QObject>
#include <QThread>
#include <QDebug>class Worker : public QObject {Q_OBJECT
public:explicit Worker(QObject *parent = nullptr) : QObject(parent) {}void doWork() {qDebug() << "工作線程中執行:" << QThread::currentThread();}
};#endif // WORKER_H

#### **`workerthread.h` — WorkerThread 類定義**```cpp
#ifndef WORKERTHREAD_H
#define WORKERTHREAD_H#include <QThread>
#include <QObject>
#include "worker.h"class WorkerThread : public QThread {Q_OBJECT
public:explicit WorkerThread(Worker *worker, QObject *parent = nullptr) : QThread(parent), m_worker(worker) {}protected:void run() override {emit workSignal();}signals:void workSignal();private:Worker *m_worker;
};#endif // WORKERTHREAD_H
main.cpp — 主函數
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include "worker.h"
#include "workerthread.h"int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);Worker worker;  // 創建工作對象WorkerThread thread(&worker);  // 創建工作線程// 連接信號和槽QObject::connect(&thread, &WorkerThread::workSignal, &worker, &Worker::doWork);thread.start();  // 啟動線程thread.wait();   // 等待線程結束return a.exec();
}

4. 條件變量(QWaitCondition)

QWaitCondition 允許線程在等待某個條件滿足時釋放鎖,并在條件滿足時喚醒其他線程。通常與 QMutex 一起使用,用于在多線程間傳遞信號和等待條件。

代碼示例:
#include <QWaitCondition>
#include <QMutex>
#include <QThread>
#include <QDebug>QMutex mutex;  // 聲明一個互斥鎖
QWaitCondition condition;  // 聲明一個條件變量
bool ready = false;  // 共享條件變量// 等待條件
void waitForCondition() {mutex.lock();while (!ready) {  // 如果條件不滿足,線程會阻塞condition.wait(&mutex);  // 等待}qDebug() << "條件滿足,線程繼續執行!";mutex.unlock();
}// 通知條件滿足
void notifyCondition() {mutex.lock();ready = true;  // 改變條件狀態condition.wakeOne();  // 喚醒一個等待的線程mutex.unlock();
}int main() {QThread* thread1 = new QThread();QThread* thread2 = new QThread();QObject::connect(thread1, &QThread::started, waitForCondition);  // 連接信號和槽QObject::connect(thread2, &QThread::started, notifyCondition);  // 連接信號和槽thread1->start();  // 啟動線程1thread2->start();  // 啟動線程2thread1->wait();  // 等待線程1結束thread2->wait();  // 等待線程2結束
}

注釋:

  • QWaitCondition 適用于需要線程等待某個條件滿足后再繼續執行的情況。thread1 會等待 ready 變量為真,然后繼續執行,而 thread2 會改變條件并喚醒 thread1

5. 原子操作(QAtomicInt 和 QAtomicPointer)

QAtomicIntQAtomicPointer 提供了線程安全的原子操作,不需要加鎖即可安全地操作共享變量。適用于計數器等簡單的數值操作。

代碼示例:
#include <QAtomicInt>
#include <QThread>
#include <QDebug>QAtomicInt counter(0);  // 聲明原子計數器// 原子遞增計數器
void incrementCounter() {counter.fetchAndAddOrdered(1);  // 原子遞增操作
}class MyThread : public QThread {
public:void run() override {for (int i = 0; i < 1000; ++i) {incrementCounter();  // 調用原子遞增}}
};int main() {MyThread thread1, thread2;  // 創建兩個線程thread1.start();  // 啟動線程1thread2.start();  // 啟動線程2thread1.wait();  // 等待線程1結束thread2.wait();  // 等待線程2結束qDebug() << "最終計數器的值:" << counter;  // 輸出計數器的最終值
}

注釋:

  • QAtomicInt 允許我們進行線程安全的計數操作,無需加鎖,適用于高效的原子操作。

線程同步方法對比

同步方法優點缺點最適用場景
QMutex簡單、直觀,適合保護共享資源每次訪問資源都需要加鎖,可能會影響性能需要保證共享資源被多個線程安全訪問的場景
QReadWriteLock允許多個線程并發讀取,適用于讀多寫少的情況寫入時需要獨占鎖,可能會導致讀取阻塞數據經常被讀取,但更新不頻繁的場景
信號與槽實現跨線程通信,不需要顯式的鎖適用于信號發出的線程和接收的線程需要有明確的關聯需要線程間通信,且希望簡化鎖操作的場景
QWaitCondition允許線程等待條件滿足時阻塞和喚醒其他線程比較適合于等待某個條件的場景,使用復雜線程需要等待特定條件發生的場景
原子操作高效、簡單,避免了使用鎖的復雜性只適用于簡單的數值或指針操作高性能計數器、指針操作等簡單數值操作的場景

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

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

相關文章

JDK-17 保姆級安裝教程(附安裝包)

文章目錄 一、下載二、安裝三、驗證是否安裝成功1、看 java 和 javac 是否可用2、看 java 和 javac 的版本號是否無問題 一、下載 JDK-17_windows-x64_bin.exe 二、安裝 三、驗證是否安裝成功 java&#xff1a;執行工具 javac&#xff1a;編譯工具 1、看 java 和 javac 是否…

【LeetCode Hot100】回溯篇

前言 本文用于整理LeetCode Hot100中題目解答&#xff0c;因題目比較簡單且更多是為了面試快速寫出正確思路&#xff0c;只做簡單題意解讀和一句話題解方便記憶。但代碼會全部給出&#xff0c;方便大家整理代碼思路。 46. 全排列 一句話題意 給定一個無重復數字的序列&#xf…

pytest-前后置及fixture運用

1.pytest中的xunit風格前后置處理 pytest中用例的前后置可以直接使用類似于unittest中的前后置處理&#xff0c;但是pytest中的前后置處理方式更 加豐富&#xff0c;分為模塊級、類級、方法級、函數級等不同等級的前后置處理&#xff0c;具體見下面的代碼&#xff1a; test_…

使用scipy求解優化問題

一、求解二次規劃問題 min(X.T * P * X C.T * X) s.t. Xi > 0 ∑Xi 1 1.定義目標函數 def objective(x):return 0.5 * np.dot(x, np.dot(P, x)) np.dot(c, x)2. 定義等式約束 def equality_constraint(x):return np.sum(x) - 1 3.定義邊界約束&#xff1a;x # …

C++初階-STL簡介

目錄 1.什么是STL 2.STL的版本 3.STL的六大組件 4.STL的重要性 4.1在筆試中 4.2在面試中 4.3.在公司中 5.如何學習STL 6.總結和之后的規劃 1.什么是STL STL&#xff08;standard template library-標準模板庫&#xff09;&#xff1b;是C標準庫的重要組成部分&#xf…

kivy android打包buildozer.spec GUI配置

這個適合剛剛學習kivyd的道友使用&#xff0c;后面看情況更新 代碼 import tkinter as tk from tkinter import ttk, filedialog, messagebox, simpledialog import configparser import os import json # 新增導入class BuildozerConfigTool:def __init__(self, master):se…

MOOS-ivp使用(一)——水下機器人系統的入門與使用

MOOS-ivp使用&#xff08;一&#xff09;——水下機器人系統的入門與使用 MOOS-ivp&#xff08;Marine Operational Oceanographic System for Intelligent Vehicle Planning&#xff09;是專為水下機器人&#xff08;如AUV&#xff09;設計的開源框架。類似于ROS&#xff0c;…

電子病歷高質量語料庫構建方法與架構項目(智能質控體系建設篇)

引言 隨著人工智能技術的迅猛發展,醫療信息化建設正經歷著前所未有的變革。電子病歷作為醫療機構的核心數據資產,其質量直接關系到臨床決策的準確性和醫療安全。傳統的病歷質控工作主要依賴人工審核,存在效率低下、主觀性強、覆蓋面有限等問題。近年來,基于人工智能技術的…

react學習筆記4——React UI組件庫與redux

流行的開源React UI組件庫 material-ui(國外) 官網: http://www.material-ui.com/#/github: GitHub - mui/material-ui: Material UI: Comprehensive React component library that implements Googles Material Design. Free forever. ant-design(國內螞蟻金服) 官網: Ant…

GPU集群搭建

1. 硬件規劃與采購 GPU 服務器&#xff1a;挑選契合需求的 GPU 服務器&#xff0c;像 NVIDIA DGX 系列就不錯&#xff0c;它集成了多個高性能 GPU。網絡設備&#xff1a;高速網絡設備不可或缺&#xff0c;例如萬兆以太網交換機或者 InfiniBand 交換機&#xff0c;以此保證節點…

ZYNQ 純PL端邏輯資源程序固化流程

ZYNQ 純PL端邏輯資源程序固化 ZYNQ的程序固化流程比傳統的FPGA固化流程復雜很多&#xff0c;Vivado生成的bit文件無法直接固化在ZYNQ芯片中。因為ZYNQ 非易失性存儲器的引腳&#xff08;如 SD 卡、QSPI Flash&#xff09;是 ZYNQ PS 部分的專用引腳。這些非易失性存儲器由 PS …

[計算機科學#6]:從鎖存器到內存,計算機存儲的構建與原理

【核知坊】&#xff1a;釋放青春想象&#xff0c;碼動全新視野。 我們希望使用精簡的信息傳達知識的骨架&#xff0c;啟發創造者開啟創造之路&#xff01;&#xff01;&#xff01; 內容摘要&#xff1a;在上一篇文章中&#xff0c;我們深入了解了計算機如…

如何刪除Google Chrome中的所有歷史記錄【一鍵清除】

谷歌瀏覽器記錄了用戶訪問過的網站。這方便了查找&#xff0c;但有時也需要清理。刪除所有歷史記錄很簡單&#xff0c;只要按照以下步驟操作。 1. 打開谷歌瀏覽器 首先要啟動谷歌瀏覽器。點擊右上角的三個點&#xff0c;進入主菜單。 2. 進入歷史記錄界面 在菜單中找到“歷史…

關于瀏覽器對于HTML實體編碼,urlencode,Unicode解析

目錄 HTML實體編碼 URL編碼 Unicode編碼 解析層次邏輯 為什么<script></script>不可以編碼符號 為什么不能編碼JavaScript:協議 為什么RCDATA標簽中的都會被解析成文本 為什么HTML編碼了<>無法執行 HTML實體編碼 通過特殊語法&#xff08;<、>…

【數據分享】2020年中國高精度森林覆蓋數據集(免費獲取)

森林作為全球陸地生態系統的主體&#xff0c;分布面積廣、結構復雜&#xff0c;承擔著調節氣候、維護生態安全、改善環境等方面的重要作用。我國的森林資源豐富&#xff0c;據《中國森林資源報告&#xff1a;2014—2018》統計&#xff0c;我國森林覆蓋率已經達到23.04%。森林覆…

C語言學習之動態內存的管理

學完前面的C語言內容后&#xff0c;我們之前給內存開辟空間的方式是這樣的。 int val20; char arr[10]{0}; 我們發現這個方式有兩個弊端&#xff1a;空間是固定的&#xff1b;同時在聲明的時候必須指定數組的長度&#xff0c;一旦確定了大小就不能調整的。 而實際應用的過程中…

【深度學習-Day 2】圖解線性代數:從標量到張量,理解深度學習的數據表示與運算

Langchain系列文章目錄 01-玩轉LangChain&#xff1a;從模型調用到Prompt模板與輸出解析的完整指南 02-玩轉 LangChain Memory 模塊&#xff1a;四種記憶類型詳解及應用場景全覆蓋 03-全面掌握 LangChain&#xff1a;從核心鏈條構建到動態任務分配的實戰指南 04-玩轉 LangChai…

首頁數據展示

排版 現在做首頁的排版&#xff0c;依舊是偷antd里面的東西 使用card包裹list的樣式 import React from react import axios import { Card, Col, Row, List } from antd import { EditOutlined, EllipsisOutlined, SettingOutlined } from ant-design/icons; import { Avat…

使用Set和Map解題思路

前言 Set和Map這兩種數據結構,在解決一些題上&#xff0c;效率很高。跟大家簡單分享一些題以及如何使用Set和Map去解決這些題目。 題目鏈接 136. 只出現一次的數字 - 力扣&#xff08;LeetCode&#xff09; 138. 隨機鏈表的復制 - 力扣&#xff08;LeetCode&#xff09; 舊…