QT跨平臺應用程序開發框架(11)—— Qt系統相關

目錄

一,事件

1.1 關于事件

1.2 處理事件

1.3 處理鼠標事件

1.3.1 點擊事件

1.3.2 釋放事件

1.3.3? 雙擊事件

1.3.4 滾輪事件

1.3.5?注意事項

1.4 處理鍵盤事件

1.5 定時器事件

1.6 窗口移動和大小改變事件

二,文件操作

2.1 文件操作概述

2.2 QFile 介紹

2.3 QFile 使用

2.4 QFileInfo 使用

三,Qt多線程

3.1 介紹

3.2 多線程版倒計時

3.3 鎖

3.4 條件變量和信號量

雖然Qt是跨平臺的 C++ 開發框架,但是Qt 的很多能力其實是操作系統提供的,只不過 Qt 封裝了系統的 API

一,事件

1.1 關于事件

用戶進行的各種操作,就可能會產生信號,可以指定槽函數,當信號觸發時,就能夠自動執行到對應的槽函數

同時,用戶的各種操作,也會產生“事件”,事件的概念和信號非常相似,同樣也可以給事件關聯上一些函數,當事件觸發時,能夠執行到對應代碼

事件與信號槽的關系:

  • 事件本身是操作系統提供的機制,Qt 也就是將其進行了封裝
  • 但是事件對應的代碼編寫起來不是很方便,所以Qt 對事件機制進行進一步的封裝,就有了信號槽
  • 所以信號槽是對于事件的進一步封裝,事件是信號槽的底層機制

實際開發中,絕大部分和用戶的交互都是通過“信號槽”來完成,但在有些特殊情況下,信號槽可能無法滿足需求,所以此時就需要重寫事件處理函數的形式,來手動處理事件的響應邏輯

常見的 Qt 事件如下:

?常見事件描述:

名稱描述
鼠標事件鼠標左鍵、鼠標右鍵、鼠標滾輪,鼠標的移動,鼠標按鍵的按下和松開
鍵盤事件按鍵類型、按鍵按下、按鍵松開
定時器事件定時時間到達
進入離開事件鼠標的進入和離開
滾輪事件鼠標滾輪滾動
繪屏事件重繪屏幕的某些部分
顯示隱藏事件窗口的顯示和隱藏
移動事件窗口位置的變化
窗口事件是否為當前窗口
大小改變事件窗口大小改變
焦點事件鍵盤焦點移動
拖拽事件用鼠標進行拖拽

1.2 處理事件

所謂處理事件,就是將事件和一段代碼關聯起來,當事件觸發時,就能執行這段代碼

之前我們通過 connect 將事件和槽關聯,但是要想關聯事件,需要重寫某個事件處理函數

下面我們演示一下鼠標進入和鼠標離開事件,假設 有一個按鈕,當鼠標移到上面時就會觸發鼠標進入事件,移開時會觸發離開事件,需要重寫的虛函數如下:

?我們先創建一個繼承 QWidget 的項目,我們可以在界面上放一個 label,當鼠標移動到 label 里時,顯示一些文字,離開 label 時顯示另一些文字:

然后我們創建一個 QLabel 的子類,然后在這個子類里重寫 enterEvent 和 leaveEvent:

然后修改下構造函數:

然后就是重寫兩個虛函數了,下面是 label.cpp 的內容:

#include "label.h"Label::Label(QWidget* parent) : QLabel(parent)
{}void Label::enterEvent(QEvent *event)
{this->setText("鼠標進來了");
}void Label::leaveEvent(QEvent *event)
{this->setText("鼠標出去了");
}

但是此時我們執行后,我們的 label 并沒有什么變化,因為,我們在ui界面通過拖拽方式創建的 Label,還是 QLabel 類型,所以我們需要提升 label 的類型,如下:

?

然后就可以處理事件了,效果如下:

1.3 處理鼠標事件

1.3.1 點擊事件

我們下面演示一下通過事件獲取鼠標點擊的位置

以1.2 中的代碼為例進行擴展,先把label進行擴大:

需要重寫的函數如下:

label.cpp 代碼如下:

#include "label.h"
#include <QMouseEvent>
#include <QDebug>Label::Label(QWidget* parent) : QLabel(parent)
{}void Label::mousePressEvent(QMouseEvent *event)
{//當前 event 就包含了鼠標的位置qDebug() << "控件里位置:" << event->x() << ", " << event->y(); //原點是控件左上角而不是窗口左上角qDebug() << "屏幕上位置:" << event->globalX() << ", " << event->globalY(); //這個是相對于 “整個屏幕” 左上角為原點的位置//這個函數其實按下左鍵、右鍵、滾輪都能觸發有些鼠標還帶有前進后退鍵,也可以觸發if(event->button() == Qt::LeftButton) qDebug() << "按下左鍵";else if(event->button() == Qt::RightButton) qDebug() << "按下右鍵";else qDebug() << "按下其它鍵";
}

效果如下:

1.3.2 釋放事件

要重寫的虛函數為:

?文件還是前面的 label.cpp ,重寫的事件函數如下:

void Label::mouseReleaseEvent(QMouseEvent *event)
{if(event->button() == Qt::LeftButton) qDebug() << "左鍵釋放";else if(event->button() == Qt::RightButton) qDebug() << "右鍵釋放";else qDebug() << "其它鍵釋放";
}

效果和上面類似:

1.3.3? 雙擊事件

要重寫的槽函數為:

重寫的函數如下:

void Label::mouseDoubleClickEvent(QMouseEvent *event)
{if(event->button() == Qt::LeftButton) qDebug() << "左鍵雙擊";else if(event->button() == Qt::RightButton) qDebug() << "右鍵雙擊";else qDebug() << "其它鍵雙擊";
}

只有當第二次按下時,才能識別為“雙擊”,所以一次雙擊按順序會觸發四個事件:按下,釋放,雙擊,釋放

1.3.4 滾輪事件

需要重寫的虛函數為:

代碼如下:

int total = 0;
void Label::wheelEvent(QWheelEvent *event) //QWheelEvent 是一個專門的鼠標滾輪的類
{total += event->delta();//可以獲取鼠標滾動了多遠qDebug() << total;
}

1.3.5?注意事項

注意一:如果想將上面鼠標的事件從label控件擴展到整個窗口,也只需要在 QWidget 類中重寫對應的虛函數即可

?注意二:關于鼠標移動事件:

  • 鼠標移動事件不同于其它的鼠標事件,只要鼠標移動,就會產生巨量的鼠標移動事件,一旦該事件的邏輯比較多,系統就容易卡頓
  • 所以 Qt 為了程序的流暢性,鼠標移動時,不會調用 mouseMoveEvent ,除非是顯示告訴 Qt 就要追蹤鼠標位置,需要在構造函數里設置:this->setMouseTracking(true);? 告訴Qt我要追蹤鼠標位置

1.4 處理鍵盤事件

按鍵事件是通過 QKeyEvent 類來實現,我們前面是通過 QShortCut 搭配?QKeySequence 的,先通過 QShortCut 綁定一個快捷鍵,當快捷鍵被按下,會產生一個信號,再通過槽進行代碼邏輯

當然,上面是信號槽機制封裝過的獲取鍵盤按鍵的方式,站在更底層的角度,也可以通過事件獲取到用戶鍵盤按下的情況的,需要重寫的虛函數如下:

重寫的函數如下:

void Label::keyPressEvent(QKeyEvent *event)
{//可以檢測單個按鍵,也可以檢測組合鍵if(event->modifiers() == Qt::ControlModifier) //判斷Ctrl鍵是否被按下{if(event->key() == Qt::Key_A)qDebug() << "Ctrl + A 被按下";}
}

?Qt::KeyboardModifier 中定義了在處理鍵盤事件時對應的修改鍵。在Qt中,鍵盤事件可以與修改鍵 ?起使用,以實現?些復雜的交互操作。KeyboardModifier中修改鍵的具體描述如下:

  • Qt::NoModifier:無修改鍵
  • Qt::ShiftModifier:Shift 鍵
  • Qt::ControlModifier:Ctrl 鍵
  • Qt::AltModifier:Alt 鍵
  • Qt::MetaModifier:Meta鍵(在Windows上指Windows鍵,在macOS上指Command鍵)
  • Qt::KeypadModifier:使用鍵盤上的數字鍵盤進行輸?時,Num Lock鍵處于打開狀態
  • Qt::GroupSwitchModifier:用于在輸入法組之間切換

1.5 定時器事件

Qt 中的定時器分為 QTimerEvent 和 QTImer 兩個類:

  • QTimerEvent類:用來描述一個定時器事件。在使用時需要通過 startTimer() 函數來開啟一個定時器,需要輸入一個以 ms 為單位的整數作為參數來表明設定的時間,返回值代表這個定時器。當到達指定時時間時,就可以在 timerEvent() 函數中獲取該定時器的編號來進行相關操作
  • QTimer類:來實現一個定時器,它提供了更高層次的編程接口,如:可以使用信號和槽,還可以設置只運行一次的定時器

我們在 ui 界面上搞兩個 Label 控件,一個每過1秒讓數字累加一次,一個每過2秒讓數字累加一次:

然后我們就可以重寫 timerEvent函數了,先在 widget.h 里聲明函數:

然后在 widget.cpp 里重寫定時器事件:

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//啟動定時器t1 = startTimer(1000);t2 = startTimer(2000);//此時這個 t1 和 t2 是一個定時器的標識,或者id//類似我們 Linux 里的文件描述符,起到身份標識的作用
}Widget::~Widget()
{delete ui;
}void Widget::timerEvent(QTimerEvent *e)
{//如果一個程序中存在多個定時器(startTimer 創建的定時器),此時每個定時器都會觸發這個 timerEvent 函數if(e->timerId() == t1){static int n1 = 1;ui->label->setText(QString::number(n1++)); //每個一秒加一次}if(e->timerId() == t2){static int n2 = 1;ui->label_2->setText(QString::number(n2++)); //每隔兩秒加一次}
}

?

使用 timerEvent 比 QTimer 復雜得多,不僅要手動管理 timerId,還需要注意多個定時器同時調用這個函數時的區分問題,所以后續開發中,使用 QTimer 即可,我們之前有介紹:QT跨平臺應用程序開發框架(6)—— 常用顯示類控件-CSDN博客

1.6 窗口移動和大小改變事件

  • moveEvent:窗口移動時觸發的事件

  • resizeEvent:窗口大小改變時觸發的事件

?直接重寫這兩個函數即可:

void Widget::moveEvent(QMoveEvent *event)
{qDebug() << event->pos(); //每次移動都打印窗口的位置,左上角在屏幕上的位置
}void Widget::resizeEvent(QResizeEvent *event)
{qDebug() << event->size(); //每次調整大小都打印目前窗口的大小
}

二,文件操作

C++文件操作:C++——IO流-CSDN博客

C語言文件操作:C語言文件操作-CSDN博客

2.1 文件操作概述

  • C語言中,我們通過 fopen 打開文件,通過 fread 和 fwrite 讀寫文件,fclose 關閉文件?
  • C++中,我們通過 fstream 打開文件,<< 和 >> 讀寫文件,close 關閉文件
  • Linux中,我們也通過原生 API 的 open 打開文件,read 和 write 讀寫文件,close 關閉文件

我們在 Qt 中也可以使用上述幾種方案來讀寫文件(Linux 需要在 Linux 系統上),但是 Qt 自己也提供了一套文件操作的 API,因為 Qt 誕生的很早,那時候 C++ 還沒有“標準化”的概念

所以下面我們都是使用Qt自己提供的這一套文件操作,因為和 QString 等 Qt 內置類進行很好的兼容和配合?

Qt 的文件操作也基本是三個:打開,讀寫,關閉,都是用的 QFile 類來完成操作,主要繼承關系如下圖:

  • QFile:用于文件操作和文件數據讀寫的類,使用 QFile 可以讀寫任意格式的件
  • QSaveFile:用于安全保存文件的類。使用 QSaveFile 保存文件時,會先把數據寫入一個臨時文件,成功提交后才將數據寫入最終的文件。如果保存過程中出現錯誤,臨時文件里的數據不會被寫入最終文件,這樣就能確保最終文件中不會丟失數據或只寫入了部分數據。在保存比較大的文件或復雜格式的文件時可以使用這個類,例如從網絡上下載文件等
  • QTemporaryFile:用于創建臨時文件的類。使用函數 QTemporaryFile::open() 就能創建一個文件名唯一的臨時文件,在 QTemporaryFile 對象被刪除時,臨時文件也被動刪除
  • QTcpSocket 和 QUdpSocket:分別實現了TCP和UDP的類
  • QSerialPort:是實現了串口通信的類,通過這個類可以實現計算機與串口設備的通信(窗口是一種比較古老的通信方式,一般是在嵌入式系統上)
  • QBluetoothSocket:用于藍牙通信的類。手機、平板計算機和筆記本電腦等移動設備都有藍牙通信模塊。通過 QBluetoothSocket 類,就可以編寫藍牙通信程
  • QProcess:用于啟動外部程序,并且可以給程序傳遞參數
  • QBuffer:以一個 QByteArray 對象作為數據緩沖區,將 QByteArray 對象當作一個I/O設備來讀寫

2.2 QFile 介紹

在 Qt 中,文件的讀寫主要是通過 QFile 類來實現,Qt 中讀寫文件的方法有:

  • 打開文件:open
  • 讀文件:read,readLine,readAll 等
  • 寫文件:write,writeData 等
  • 關閉文件:close

更多的操作可以在文檔中查詢關鍵詞 QFile 了解:

①打開:open?

open 有好幾個版本,比如下面兩個:

一個是 FILE*,一個是文件描述符,用起來比較麻煩,所以我們一般用的都是這個:

構造函數中,只需要指定路徑后用 open 直接打開即可,OpenMode 是打開的方式,有讀方式,寫方式,追加寫等方式,要詳細了解直接在文檔里搜索 OpenMode 即可

②讀文件:read,readLine,readAll

在 QIODevice 類里可以找到讀文件相關函數的介紹:

?③寫文件:write

④關閉文件:close

2.3 QFile 使用

我們再次創建 mainwindows 項目,直接通過代碼去構造界面

先是 mainwindow.h ,在里面聲明函數:

然后是 mainwindow.cpp 的代碼:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPlainTextEdit>
#include <QFileDialog>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//獲取到菜單欄QMenuBar* menuBar = this->menuBar();//添加菜單QMenu* m = new QMenu("文件");menuBar->addMenu(m);//添加菜單項QAction* a1 = new QAction("打開");QAction* a2 = new QAction("保存");m->addAction(a1);m->addAction(a2);//指定一個輸入框edit = new QPlainTextEdit();this->setCentralWidget(edit);//把字體放大一些QFont font;font.setPixelSize(20);edit->setFont(font);//連接 QAction 的信號槽connect(a1, &QAction::triggered, this, &MainWindow::handleAction1);connect(a2, &QAction::triggered, this, &MainWindow::handleAction2);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::handleAction1()
{//1,先彈出一個“打開文件”的對話框,選擇文件QString path = QFileDialog::getOpenFileName(this);//2,將文件名顯示到狀態欄里(也可以搞一個 label,然后放進去)QStatusBar* s = this->statusBar(); //獲取狀態欄s->showMessage(path);//3,根據文件路徑構建 QFile 對象QFile file(path);bool ret = file.open(QIODevice::ReadOnly); //以只讀方式打開文件if(!ret){s->showMessage("路徑錯誤或文件不存在!");return;}//4,讀取文件QString text =  file.readAll();//可以直接用 QString 來接收,因為Qstring 重載了構造函數//可以用 QByteArray,也就是 readAll 的返回值對象來構造 QString 對象//但是這樣需要確保打開的是一個文本文件才行,如果是二進制文件,交給 QString 就不合適了//因為二進制文件沒有限制,圖片,視頻等都可以//文本文件必須得是合法字符(指的是遵循 utf8,gbk等編碼方式)//5,關閉文件file.close();//6,將讀取的內容顯示在輸入框中edit->setPlainText(text);
}void MainWindow::handleAction2()
{//1,先彈出“保存文件”對話框QString path = QFileDialog::getOpenFileName(this);//2,再狀態欄中顯示文件名QStatusBar* s = this->statusBar();s->showMessage(path);//3,根據用戶選擇的路徑,構造 QFile 對象,并打開文件QFile file(path);bool ret = file.open(QFile::WriteOnly); //只寫方式if(!ret){s->showMessage("路徑錯誤或文件不存在!");return;}//4,寫文件const QString& text = edit->toPlainText();file.write(text.toUtf8());//5,關閉文件file.close();
}

這樣就完成了一個簡單的針對文本文件的打開修改和保存的窗口了?

2.4 QFileInfo 使用

對于文件不僅僅只有讀寫,還有例如獲取屬性的一系列操作,如下:

  • isDir():檢查該文件是否是目錄?
  • isExecutable():檢查該文件是否是可執行文件
  • fileName():獲得文件名
  • completeBaseName():獲取完整的文件名
  • suffix():獲取文件后綴名
  • completeSuffix():獲取完整的文件后綴
  • size():獲取文件大小
  • isFile():判斷是否為文件
  • fileTime():獲取文件創建時間、修改時間、最近訪問時間等

?我們可以通過 QFileInfo 獲取到 Qt 的文件的相關屬性,下面我們直接創建一個按鈕,要求是點擊按鈕后,打開文件選擇窗口,選擇好文件后,打印文件的信息,按鈕槽函數如下:

void MainWindow::on_pushButton_clicked()
{//彈出文件對話框,并獲取文件屬性信息QString path = QFileDialog::getOpenFileName(this);QFileInfo f(path);qDebug() << f.fileName();qDebug() << f.suffix();qDebug() << f.path();qDebug() << f.size();qDebug() << f.isFile();qDebug() << f.isDir();
}

三,Qt多線程

3.1 介紹

Qt 多線程概念和 Linux 本質沒有區別,可以參考:

  • Linux系統編程——線程基本概念-CSDN博客
  • Linux系統編程——線程同步互斥與線程安全-CSDN博客

Linux原生的多線程 API,了解即可,因為使用起來很麻煩,可以看到上面兩篇文章里的多線程代碼很長也很難理解,所以實際開發中很少使用 原生的線程 API

Qt 中的多線程 API,參考了 Java 中線程庫 API 的設計方式?

在 Qt 中,多線程的處理一般通過 QThread類 來實現,它代表一個在應用程序中可以獨立控制的線程,也可以和進程中的其它線程共享數據

總的來說 QThread對象 用于管理程序中的一個線程,常用 API 如下:

API說明
run()

線程的入口函數

  • 一般是創建一個 QThread 的子類,然后重寫 run 函數,以此起到入口函數的作用
start()

通過調用 run() 開始執行線程

  • 操作系統將根據優先級參數調度線程
  • 如果線程已在運行,這個函數什么也不做
currentThread()返回?個指向管理當前執行線程的 QThread 的指針
isRunning()

如果線程正在運行則返回 true

否則返回false

sleep() / msleep() / usleep()使線程休眠,單位為秒 / 毫秒 / 微秒
wait()

阻塞線程,直到滿足以下任何?個條件:

  • 與此QThread對象關聯的線程已經完成執行 (即當它從 run() 返回時),如果線程已經完成,這個函數將返回true,如果線程尚未啟動,它也返回true
  • 已經過了幾毫秒。如果時間是ULONG_MAX(默認值),那么等待永遠不會超時 (線程必須從run()返回),如果等待超時,此函數將返回false。
  • 這提供了與POSIXpthread_join()函數類似的功能。
terminate()

終止線程的執行。線程可以立即終止,也可以不立即終止,這取決于操作系統的調度策略

在 terminate ()之后使用QThread::wait()來確保。

finished()當線程結束時會發出該信號,可以通過該信號來實現線程的清理工作

3.2 多線程版倒計時

我們之前使用定時器搞過一個倒計時這樣的程序:QT跨平臺應用程序開發框架(6)—— 常用顯示類控件-CSDN博客

咱們也可以通過線程來完成這樣的功能,先創建一個人基于 QWidget 的項目,然后創建QWidget 的子類,在 thread.h 中聲明下 run 函數,這時候就可以在 .cpp 里重寫 run 函數了 :

然后就是編寫線程的run函數邏輯了?

注意,此時不能直接在 run 中修改界面內容,前面說過,由于存在線程安全問題,Qt 規定只有主線程才能對界面控件進行修改?

如下 thread.cpp 的代碼:

#include "thread.h"Thread::Thread()
{}void Thread::run()
{//不能修改界面,但是可以計時,每過一秒鐘,通過信號槽,通知主線程去更新頁面for(int i = 0; i < 10; i++){sleep(1);//發送一個信號去通知主線emit notify();}
}

?創建另一個線程,在新線程中進行計時,每循環一次就是 sleep(1),然后就可以更新界面了

下面是 widget.cpp 的代碼:

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);connect(&thread, &Thread::notify, this, &Widget::handle); //將自定義函數綁定 widgetthread.start(); //啟動線程,start() 其實就是去調用 run()
}Widget::~Widget()
{delete ui;
}void Widget::handle() //記得在 .h 中聲明
{//此處修改頁面內容即可int value = ui->lcdNumber->intValue();if(value > 0) value--;ui->lcdNumber->display(value);
}

應用場景:

  • 我們在 Linux 系統里學習多線程,主要是站在服務器開發角度來看待的:Linux 多線程使用的目的,是為了充分利用多核 CPU 的計算找資源,因為一些高性能服務器可能有2個甚至更多個 CPU
  • 但是對于客戶端來說,用戶的“使用體驗”是很重要的,如果“非常快”的代價是“系統很卡”,就會降低使用體驗,所以客戶端上的程序很少會用多線程把 CPU 資源占完,畢竟用戶的個人電腦手機等不僅僅只運行你一個程序
  • 所以客戶端中的多線程,主要用于執行一個耗時的等待 IO 的操作,避免主線程長時間等待 IO 時卡死,比如客戶端 上傳/下載 一個很大的文件,需要長時間傳輸,這時候就可以用線程來執行下載操作而不會導致頁面卡死了

3.3 鎖

談到多線,就不得不提到“線程安全”這個大話題,3.1 介紹 的兩篇文章已經介紹了 線程安全問題的一系列原因后果和解決方法,這里不再贅述

加鎖是解決線程安全的最簡單的辦法,Qt 同樣也提供了對于的鎖 QMutex,來針對系統的鎖進行封裝,主要也提供了兩種方法:lock 和 unclock ,負責加鎖和解鎖,下面來演示一下:

下面是 thread.h 的代碼,主要包括:聲明run函數,添加靜態變量和靜態鎖:

#ifndef THREAD_H
#define THREAD_H#include <QWidget>
#include <QThread>
#include <QMutex>class Thread : public QThread
{Q_OBJECT
public:Thread();void run(); //重要的是重寫父類的run函數//添加一個 static 成員變量,然后讓兩個線程都去修改這個變量static int num;static QMutex mutex;};#endif // THREAD_H

重寫run函數,讓其對 num 進行加加操作:

void Thread::run()
{for(int i = 0; i < 50000; i++){mutex.lock();num++;mutex.unlock();}
}

?然后我們的 主邏輯如下:

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);Thread t1;Thread t2;t1.start();t2.start();//加上等待,讓主線程等待這倆線程執行結束t1.wait();t2.wait();qDebug() << Thread::num;
}

上面只是鎖的最簡單的用法,鎖和動態內存一樣,都需要手動釋放,但是一旦中間有個 if跳過了釋放邏輯,或者直接拋異常了,就會造成死鎖問題

動態內存無法釋放的問題,我們一般用智能指針來搞:C++——C++11智能指針_智能指針c++11-CSDN博客?

  • 在C++中,對于鎖的釋放,C++11 引入了 std::lock_guard,相當于是 std::mutex 的智能指針,借助了 RAII 機制
  • Qt 也提供了類似的設計,用到的是 QMutexLocker 類,所以下面我們來調一下 run 函數的代碼
void Thread::run()
{for(int i = 0; i < 50000; i++){QMutexLocker locker(&mutex);//mutex.lock();num++;//mutex.unlock();}
}

3.4 條件變量和信號量

關于條件變量,3.1 的第二個鏈接文章的最后一個標題已經介紹過,而關于信號量:Linux系統編程——進程間通信(管道與共享內存)_共享內存 管道-CSDN博客

多個線程之間的調度是無序的,所以我們為了能夠一定程度上干預線程的執行順序,引入了條件變量,Qt 中的條件變量通過 QWaitCondition 來實現,提供了 wait 和 wake,標識等待和喚醒,還有一個 wakeAll ,喚醒所有線程

注意:只有在 mutex.lock() 后才能 wait 等待,因為 wait 函數會先釋放鎖,然后再等待,所以要想釋放鎖,必須先得到鎖?

這兩個和我們 Linux 中的使用方式基本一致,只是 API 不一樣,這里就不演示了?:Linux系統編程——線程同步互斥與線程安全-CSDN博客

API 本身的使用并不麻煩,更重要的是 API 背后的運行邏輯等?

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

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

相關文章

sqli-labs通關筆記-第11關 POST字符型注入(單引號閉合 手工注入+腳本注入兩種方法)

目錄 一、字符型注入 二、limit函數 三、GET方法與POST方法 四、源碼分析 1、代碼審計 2、SQL注入安全分析 五、滲透實戰 1、進入靶場 2、注入點分析 &#xff08;1&#xff09;SQL語句 &#xff08;2&#xff09;萬能密碼登錄 3、手工注入 &#xff08;1&#xf…

網絡安全基礎作業三

回顧web前端的代碼<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>用戶登錄</title><st…

基于單片機的溫濕度報警系統設計與實現

摘 要 本項研究對溫濕度警報系統的需求進行了詳盡分析&#xff0c;并成功研制出一套以單片機為技術核心的溫濕度警報系統。該系統由硬件搭建和軟件編程兩大模塊構成。在硬件搭建方面&#xff0c;系統整合了STM32主控芯片、DS18B20溫度傳感器、濕敏電阻、按鍵組件、OLED顯示屏、…

(八)復習(拆分微服務)

文章目錄項目地址一、Ticketing模塊拆分1.1 創建web api1. 添加引用2. 添加需要的包和配置3. program.cs4. docker-compose修改項目地址 教程作者&#xff1a;教程地址&#xff1a; 代碼倉庫地址&#xff1a; 所用到的框架和插件&#xff1a; dbt airflow一、Ticketing模塊拆…

DearMom以“新生兒安全系統”重塑嬰兒車價值,攬獲CBME雙項大獎

7月16日&#xff0c;在剛剛開幕的2025 CBME中國孕嬰童展上&#xff0c;備受矚目的CBME中國孕嬰童產業獎正式揭曉。深耕嬰兒車品類的專業品牌DearMom&#xff0c;憑借其卓越的創新實力與對新生兒安全出行的深刻洞察&#xff0c;一舉摘得重量級獎項——“杰出品牌創新獎”。同時&…

瀚高數據庫開啟Oracle兼容模塊

文章目錄環境癥狀問題原因解決方案環境 系統平臺&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;4.5 癥狀 不能使用Oracle兼容&#xff1b; 問題原因 在瀚高數據庫V45中oracle兼容模塊需要單獨開啟默認是關閉狀態。 解決方案 使用sysdba執行修改…

final修飾符不可變的底層

final修飾符的底層原理在 Java 中&#xff0c;final 修飾符的底層實現涉及 編譯器優化 和 JVM 字節碼層面的約束其核心目標是保證被修飾元素的【不可變性】或 【不可重寫 / 繼承性】一、final 修飾類&#xff1a;禁止繼承的底層約束當一個類被 final 修飾時&#xff0c;例如 St…

如何排查服務器 CPU 飆高

服務器 CPU 飆高&#xff08;CPU 使用率持續超過 80% 甚至接近 100%&#xff09;是典型的性能瓶頸問題&#xff0c;可能由應用邏輯缺陷、資源競爭、外部壓力或硬件/系統異常引起。以下是系統化的排查步驟&#xff0c;覆蓋從現象確認到根因定位的全流程。?一、確認 CPU 飆高的現…

【DataWhale】快樂學習大模型 | 202507,Task05筆記

前言 今天是Transformer的編碼實戰階段&#xff0c;照著示例代碼執行一遍吧 embedding self.tok_embeddings nn.Embedding(args.vocab_size, args.dim)把token向量轉為embedding矩陣&#xff08;一個token一個embedding向量&#xff09; 位置編碼 為了解決“我喜歡你”和…

用ffmpeg 進行視頻的拼接

author: hjjdebug date: 2025年 07月 22日 星期二 17:06:02 CST descrip: 用ffmpeg 進行視頻的拼接 文章目錄1. 指定協議為concat 方式.1.1 協議為concat 模式,會調用 concat_open 函數1.2 當讀數據時,會調用concat_read2. 指定file_format 為 concat 方式2.1 調用concat_read_…

HTTP與HTTPS技術細節及TLS密鑰交換與證書校驗全流程

HTTP與HTTPS技術細節及TLS密鑰交換與證書校驗全流程 引言 文檔目的與范圍 核心技術棧概述 本文檔的核心技術棧圍繞傳輸層安全協議&#xff08;TLS&#xff09;展開。TLS協議作為安全套接字層&#xff08;SSL&#xff09;的后繼標準&#xff0c;是現代網絡安全通信的基礎&am…

廣播分發中心-廣播注冊流程

廣播是怎么注冊的呢&#xff1f;階段組件/數據結構作用描述存儲位置/關聯關系App進程階段BroadcastReceiver開發者自定義的廣播接收器&#xff0c;實現onReceive方法處理事件。App進程&#xff08;Activity/Service等組件內&#xff09;ReceiverDispatcher將BroadcastReceiver封…

OpenCV計算機視覺實戰(16)——圖像分割技術

OpenCV計算機視覺實戰&#xff08;16&#xff09;——圖像分割技術0. 前言1. 分水嶺算法1.1 應用場景1.2 實現過程2. GrabCut 交互式分割2.1 應用場景2.2 實現過程3. FloodFill3.1 應用場景3.2 實現過程小結系列鏈接0. 前言 圖像分割是計算機視覺中將像素劃分為具有特定語義或…

Coturn打洞服務器

* 概念理解&#xff1a;1. SDP協議&#xff1a;會話描述協議&#xff0c;視頻通話的雙方通過交換SDP信息進行媒體協商&#xff0c;從而選擇使用某一相同的媒體協議進行通信&#xff1b;TLS協議&#xff1a;基于TCP的安全層傳輸協議DTLS協議&#xff1a;基于UDP的安全層傳輸協議…

python flusk 監控

# 創建虛擬環境目錄 python3 -m venv /sda1/xunjian/venv # 激活虛擬環境 source /sda1/xunjian/venv/bin/activate # 激活后終端會顯示 (venv)創建虛擬環境&#xff08;在當前目錄&#xff09;&#xff1a;bashpython3 -m venv venv激活虛擬環境&#xff1a;bashsource venv/b…

VUE2 項目學習筆記 ? 語法 v-if/v-show

?語法頁面渲染的時候&#xff0c;需要服務器傳過來的對象中的一個屬性&#xff0c;然后根據這個屬性用v-for渲染標簽&#xff0c;這里寫的v-for".... in dataList.goodsList"但是當解析到這行語法的時候&#xff0c;dataList還沒返回&#xff0c;因此控制臺會報錯找…

使用qemu命令啟動虛擬機

1. 安裝相關軟件 yum install qemu edk2* libvirt -y 啟動libvirt服務 systemctl start libvirtd systemctl status libvirtd2. 創建虛擬機 2.1. qemu啟動命令示例 /usr/bin/qemu-system-loongarch64 \-machine virt,accelkvm \-nodefaults \-m 2048 \-smp 2,maxcpus4,co…

大模型系統化學習路線

人工智能大模型系統化學習路線一、基礎理論筑基&#xff08;1-2個月) 目標&#xff1a;建立大模型核心認知框架 核心內容&#xff1a; 深度學習基礎&#xff1a;神經網絡原理、CNN/RNN結構、梯度下降算法大模型本質&#xff1a;Transformer架構&#xff08;重點掌握注意力機制、…

LLaMA-Factory 微調可配置的模型基本參數

LLaMA-Factory 微調可配置的模型基本參數 flyfish 基本參數 一、模型加載與路徑配置參數名類型描述默認值model_name_or_pathOptional[str]模型路徑&#xff08;本地路徑或 Huggingface/ModelScope 路徑&#xff09;。Noneadapter_name_or_pathOptional[str]適配器路徑&#xf…

Ubuntu 22 安裝 ZooKeeper 3.9.3 記錄

Ubuntu 22 安裝 ZooKeeper 3.9.3 記錄 本文記錄在 Ubuntu 22.04 系統上安裝 ZooKeeper 3.9.3 的過程&#xff0c;包含 Java 環境準備、配置文件調整、啟動與停機操作、以及如何將 ZooKeeper 注冊為系統服務。 一、準備環境 ZooKeeper 3.9.x 要求 Java 11 或更高版本&#xff…