您好,登錄后才能下訂單哦!
這篇文章主要講解了“怎么使用QSemaphore進(jìn)行多線程數(shù)據(jù)同步”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“怎么使用QSemaphore進(jìn)行多線程數(shù)據(jù)同步”吧!
20210127:在生產(chǎn)者、消費者的方法中添加線程掛起方法QThread::usleep(10),使ui不卡。
20210128:在添加Track類(保存生產(chǎn)者Producer生成的每組數(shù)據(jù)),在ui界面中使用model-view同步顯示生產(chǎn)者生成的數(shù)據(jù),model-view不會對主線程造成卡頓。對消費者同樣創(chuàng)建view,還沒有進(jìn)行model綁定。
避免引起主線程的阻塞,Qt在子線程中處理大數(shù)據(jù),當(dāng)多個子線程需要處理同一塊數(shù)據(jù)時,需要使用數(shù)據(jù)同步,避免出現(xiàn)調(diào)用錯亂情況,在這里我們在兩個子線程使用QSemaphore作為標(biāo)志位,對數(shù)組進(jìn)行標(biāo)識,生產(chǎn)者線程將生成的資源存入數(shù)組,消費者數(shù)組消耗數(shù)組內(nèi)的資源,當(dāng)有一方的速度過快導(dǎo)致數(shù)組資源耗盡時,該子線程被阻塞,直到有資源時子線程繼續(xù)。代碼如下:
在全局變量中聲明數(shù)組、數(shù)組大小、資源總量:
constant_variable.h
#ifndef CONSTANT_VARIABLE_H #define CONSTANT_VARIABLE_H // 所有變量在h文件中均是聲明,定義在cpp文件中 /***************************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-24 @brief h文件聲明extern變量,cpp文件定義變量 ******************************************************************************************/ #include <QSemaphore> /*************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-24 @brief 設(shè)置循環(huán)保存數(shù)據(jù)的數(shù)組大小,相當(dāng)于設(shè)置“緩存”大小 ***************************************************************************/ extern const int BUFFER_SIZE; /*************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @date 2021-01-24 @brief 要讀取的總資源數(shù)量,數(shù)據(jù)量大,無法一次讀取完,需要長時間,分批讀取 ***************************************************************************/ extern const int DATA_SIZE; /*************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @date 2021-01-24 @brief 循環(huán)使用,保存數(shù)據(jù)資源的數(shù)組 ***************************************************************************/ extern char BUFFER[]; /*************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @date 2021-01-24 @brief 標(biāo)識Producer可以保存資源的空余位置的數(shù)量,保存在數(shù)組中的數(shù)據(jù)量,因為初 始狀態(tài)數(shù)組中沒有任何數(shù)據(jù),數(shù)組有DATA_SIZE個資源可用 ***************************************************************************/ extern QSemaphore PRODUCER_SPACE; /*************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @date 2021-01-24 @brief 標(biāo)識Consumer可以使用的已保存資源的數(shù)量,因為初始時沒有數(shù)據(jù)可供Consumer 使用,初始資源有0個 ***************************************************************************/ extern QSemaphore CONSUMER_SPACE; #endif // CONSTANT_VARIABLE_H
constant_variable.cpp
#include "constant_variable.h" const int BUFFER_SIZE(4096); const int DATA_SIZE(100000); // 定義數(shù)組大小為BUFFER_SIZE char BUFFER[BUFFER_SIZE]; // 初始時可用資源位BUFFER_SIZE QSemaphore PRODUCER_SPACE(BUFFER_SIZE); QSemaphore CONSUMER_SPACE(0);
創(chuàng)建窗體:
Producer按鈕生成資源,保存到數(shù)組中,在左側(cè)文本框中顯示Producer所在的線程及生成的資源。Consumer按鈕消耗數(shù)組中的資源,在左側(cè)文本框中顯示Consumer所在的線程及消耗的資源。這兩個按鈕是單獨運行。Both_start_PushButton按鈕同時啟動兩個子線程,一個生產(chǎn)資源,一個消耗資源,生產(chǎn)和消耗的都在左邊的文本框中顯示。
窗口代碼如下:
producer_consumer_dialog.ui 代碼:
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>Producer_consumer_dialog</class> <widget class="QDialog" name="Producer_consumer_dialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>722</width> <height>451</height> </rect> </property> <property name="windowTitle"> <string>Dialog</string> </property> <layout class="QVBoxLayout" name="verticalLayout_3"> <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QPlainTextEdit" name="producer_plainTextEdit"/> </item> <item> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QPushButton" name="producer_pushButton"> <property name="text"> <string>Producer</string> </property> </widget> </item> <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeType"> <enum>QSizePolicy::Expanding</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> </layout> </item> <item> <widget class="QTableView" name="producer_tableView"/> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QPlainTextEdit" name="consumer_plainTextEdit"/> </item> <item> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> <widget class="QPushButton" name="consumer_pushButton"> <property name="text"> <string>Consumer</string> </property> </widget> </item> <item> <spacer name="verticalSpacer_2"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> </layout> </item> <item> <widget class="QTableView" name="consumer_tableView"/> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>348</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QPushButton" name="both_start_pushButton"> <property name="text"> <string>Both_start_PushButton</string> </property> </widget> </item> </layout> </item> </layout> </widget> <resources/> <connections/> </ui>
producer_consumer_dialog.h 文件:
#ifndef PRODUCER_CONSUMER_DIALOG_H #define PRODUCER_CONSUMER_DIALOG_H #include <QDialog> #include <QThread> QT_BEGIN_NAMESPACE class Producer_move_to_thread; class Consumer_move_to_thread; class Producer_table_model; //class QCloseEvent; QT_END_NAMESPACE namespace Ui { class Producer_consumer_dialog; } /***************************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-27 @brief 創(chuàng)建主窗口,生成創(chuàng)造者producer,并在子線程-1生產(chǎn)資源。生成消費者consumer,并在子線程-2消耗生 產(chǎn)資源。資源均存儲在BUFFER[]中 ******************************************************************************************/ class Producer_consumer_dialog : public QDialog { Q_OBJECT public: explicit Producer_consumer_dialog(QWidget *parent = nullptr); ~Producer_consumer_dialog(); protected: virtual void closeEvent(QCloseEvent* ev); private: /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-27 @brief 初始化窗體控件 ***************************************************************************/ void init_widgets(); /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-27 @brief 初始化私有變量,將producer_,consumer_放入各自的子線程 ***************************************************************************/ void init_instances(); /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-27 @brief 實例化信號槽 ***************************************************************************/ void init_connections(); private slots: /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-27 @brief 將字符串content設(shè)置到文本框producer_plainTextEdit @param content 字符串 ***************************************************************************/ void set_producer_plain_text(quint32 serial_number, quint64 thread_id, char ch); /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-27 @brief 將字符串content設(shè)置到文本框consumer_plainTextEdit @param content 字符串 ***************************************************************************/ void set_consumer_plain_text(QString content); private: Ui::Producer_consumer_dialog *ui; Producer_move_to_thread* producer_; Consumer_move_to_thread* consumer_; QThread producer_thread_; QThread consumer_thread_; /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-28 @brief 消費者產(chǎn)生數(shù)據(jù)對應(yīng)的模型 ***************************************************************************/ Producer_table_model* producer_table_model_; }; #endif // PRODUCER_CONSUMER_DIALOG_H
producer_consumer_dialog.cpp文件:
#include "producer_consumer_dialog.h" #include "ui_producer_consumer_dialog.h" #include "producer_move_to_thread.h" #include "consumer_move_to_thread.h" #include "producer_table_model.h" Producer_consumer_dialog::Producer_consumer_dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Producer_consumer_dialog) { ui->setupUi(this); init_widgets(); init_instances(); init_connections(); } Producer_consumer_dialog::~Producer_consumer_dialog() { delete producer_; delete consumer_; delete producer_table_model_; delete ui; } void Producer_consumer_dialog::closeEvent(QCloseEvent *ev) { // QThread::sleep(3); if ( producer_thread_.isRunning()) { producer_thread_.quit(); producer_thread_.wait(); } if ( consumer_thread_.isRunning()) { consumer_thread_.quit(); consumer_thread_.wait(); } QDialog::closeEvent(ev); } void Producer_consumer_dialog::init_widgets() { // setFixedSize(sizeHint()); resize(sizeHint()); } void Producer_consumer_dialog::init_instances() { producer_ = new Producer_move_to_thread(); producer_->moveToThread(&producer_thread_); producer_thread_.start(); consumer_ = new Consumer_move_to_thread(); consumer_->moveToThread(&consumer_thread_); consumer_thread_.start(); producer_table_model_ = new Producer_table_model(); ui->producer_tableView->setModel(producer_table_model_); } void Producer_consumer_dialog::init_connections() { connect(ui->producer_pushButton, &QPushButton::clicked, producer_, &Producer_move_to_thread::create_char_in_thread); connect(ui->consumer_pushButton, &QPushButton::clicked, consumer_, &Consumer_move_to_thread::create_char_in_thread); connect(ui->both_start_pushButton, &QPushButton::clicked, ui->producer_pushButton, &QPushButton::clicked); connect(ui->both_start_pushButton, &QPushButton::clicked, ui->consumer_pushButton, &QPushButton::clicked); // 獲取producer_生成的字符串 connect(producer_, &Producer_move_to_thread::signal_index_id_content, this, &Producer_consumer_dialog::set_producer_plain_text); connect(producer_, &Producer_move_to_thread::signal_index_id_content, producer_table_model_, &Producer_table_model::add_data_to_track_list); // 獲取consumer_取得的字符串 connect(consumer_, &Consumer_move_to_thread::signal_string, this, &Producer_consumer_dialog::set_consumer_plain_text); } void Producer_consumer_dialog::set_producer_plain_text(quint32 serial_number, quint64 thread_id, char ch) { QString content = QString("%1: Producer thread id: %2, char: %3"). arg(serial_number). arg(thread_id). arg(ch); ui->producer_plainTextEdit->appendPlainText(content); // ui->producer_plainTextEdit->setPlainText(content); } void Producer_consumer_dialog::set_consumer_plain_text(QString content) { ui->consumer_plainTextEdit->appendPlainText(content); // ui->consumer_plainTextEdit->setPlaceholderText(content); }
生產(chǎn)類
producer_move_to_thread.h 文件:
#ifndef PRODUCER_MOVE_TO_THREAD_H #define PRODUCER_MOVE_TO_THREAD_H #include <QObject> /***************************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-28 @brief 生產(chǎn)者,會被移動到子線程中產(chǎn)生資源 ******************************************************************************************/ class Producer_move_to_thread : public QObject { Q_OBJECT public: explicit Producer_move_to_thread(QObject *parent = nullptr); ~Producer_move_to_thread(); signals: // void signal_string(quint32 serial_number, quint64 thread_id, char ch); void signal_index_id_content(quint32 index, int id, char ch); public slots: void create_char_in_thread(); }; #endif // PRODUCER_MOVE_TO_THREAD_H
producer_move_to_thread.cpp 文件:
#include <QtDebug> #include <QThread> #include <QtGlobal> #include "producer_move_to_thread.h" #include "constant_variable.h" Producer_move_to_thread::Producer_move_to_thread(QObject* parent): QObject(parent) { } Producer_move_to_thread::~Producer_move_to_thread() { } void Producer_move_to_thread::create_char_in_thread() { for (int index(0); DATA_SIZE != index; ++index) { // 生產(chǎn)1個資源(總共要產(chǎn)生的DATA_SZIE個資源),然后保存到BUFFER數(shù)組,BUFFER數(shù)組空余位置少1個 PRODUCER_SPACE.acquire(1); // 計算獲取資源序號、線程號、數(shù)據(jù)資源 quint32 serial_number = (quint32) (index + 1); quint64 thread_id = (quint64) QThread::currentThreadId(); char ch = "ABCDXYZ"[uint(std::rand()) % 7]; BUFFER[index % BUFFER_SIZE] = ch; qDebug()<< "Produce thread id: " << thread_id << ", char: " << ch; // 將數(shù)據(jù)傳給窗體Producer_consumer_dialog // emit signal_string(serial_number, thread_id, ch); emit signal_index_id_content(serial_number, thread_id, ch); // 線程掛起,不對主線程造成卡頓 QThread::usleep(10); // 數(shù)組中已保存1個字符,Consumer獲取1個可用資源 CONSUMER_SPACE.release(1); } }
資源消耗類
consumer_move_to_thread.h 文件:
#ifndef CONSUMER_MOVE_TO_THREAD_H #define CONSUMER_MOVE_TO_THREAD_H #include <QObject> class Consumer_move_to_thread : public QObject { Q_OBJECT public: explicit Consumer_move_to_thread(QObject *parent = nullptr); signals: void signal_string(QString content); public slots: void create_char_in_thread(); }; #endif // CONSUMER_MOVE_TO_THREAD_H
consumer_move_to_thread.cpp文件:
#include <QtDebug> #include <QThread> #include "consumer_move_to_thread.h" #include "constant_variable.h" Consumer_move_to_thread::Consumer_move_to_thread(QObject *parent) : QObject(parent) { } void Consumer_move_to_thread::create_char_in_thread() { for (int index(0); DATA_SIZE != index; ++index) { // 消耗1個資源(總共要消耗DATA_SZIE個資源),從BUFFER數(shù)組中取出,BUFFER數(shù)組空余位置多1個。當(dāng) // 沒有資源提供時,進(jìn)程被掛起,待有足夠資源后再執(zhí)行后續(xù)程序 CONSUMER_SPACE.acquire(1); qDebug()<< "Consumer thread id: " << (quint64) QThread::currentThreadId() << ", char: " << (char) BUFFER[index % BUFFER_SIZE]; QString content = QString("%1: Consumer thread id: %2, char: %3"). arg(index + 1). arg((quint64) QThread::currentThreadId()). arg(BUFFER[index % BUFFER_SIZE]); emit signal_string(content); //線程掛起,不對主線程造成卡頓 QThread::usleep(10); // 使用數(shù)組中1個資源,數(shù)組釋放1個空間,給生產(chǎn)者提供1個空余位置 PRODUCER_SPACE.release(1); } }
保存生產(chǎn)者生成的數(shù)據(jù)Track類:
Track.h
#ifndef TRACK_H #define TRACK_H #include <QtGlobal> /***************************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-28 @brief 數(shù)據(jù)類,保存隨機(jī)生成的資源 ******************************************************************************************/ class Track { public: /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-28 @brief 構(gòu)造函數(shù) @param index 數(shù)據(jù)的生成序列號 @param thread_id 生成數(shù)據(jù)的線程號 @param ch 生成的資源數(shù)據(jù) ***************************************************************************/ explicit Track(quint32 index = 0, quint64 thread_id = 0, char ch = 'A'); ~Track(); /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-28 @brief 拷貝構(gòu)造函數(shù) ***************************************************************************/ Track(const Track& r_value); /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-28 @brief 賦值操作符 ***************************************************************************/ Track& operator = (const Track& r_value); quint32 get_index() const; quint64 get_thread_id() const; char get_char() const; private: quint32 index_; quint64 thread_id_; char char_; }; #endif // TRACK_H
Track.cpp
#include "track.h" Track::Track(quint32 index, quint64 thread_id, char ch): index_(index), thread_id_(thread_id), char_(ch) { } Track::~Track() { } Track::Track(const Track &r_value) { index_ = r_value.index_; thread_id_ = r_value.thread_id_; char_ = r_value.char_; } Track &Track::operator =(const Track &r_value) { if (this != &r_value) { index_ = r_value.index_; thread_id_ = r_value.thread_id_; char_ = r_value.char_; } return *this; } quint32 Track::get_index() const { return index_; } quint64 Track::get_thread_id() const { return thread_id_; } char Track::get_char() const { return char_; }
生產(chǎn)者模型Produer_table_model類:
Produer_table_model.h
#ifndef PRODUCER_TABLE_MODEL_H #define PRODUCER_TABLE_MODEL_H #include <QAbstractTableModel> #include <QObject> #include "track.h" /***************************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-28 @brief ******************************************************************************************/ class Producer_table_model : public QAbstractTableModel { Q_OBJECT public: explicit Producer_table_model(QObject* parent = nullptr); ~Producer_table_model(); void add_data_to_track_list(quint32 serial_number, quint64 thread_id, char ch); QList<Track>* get_track_list(); protected: virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual int columnCount(const QModelIndex& parent = QModelIndex()) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; private: QList<Track>* track_list_; }; #endif // PRODUCER_TABLE_MODEL_H
Produer_table_model.cpp
#include "producer_table_model.h" Producer_table_model::Producer_table_model(QObject* parent): QAbstractTableModel(parent), track_list_(new QList<Track>()) { } Producer_table_model::~Producer_table_model() { delete track_list_; } void Producer_table_model::add_data_to_track_list(quint32 serial_number, quint64 thread_id, char ch) { track_list_->append(Track(serial_number, thread_id, ch)); QModelIndex header_model_index = createIndex(serial_number - 1, 0); QModelIndex tail_model_index = createIndex(serial_number - 1, 2); layoutChanged(); } QList<Track> *Producer_table_model::get_track_list() { return track_list_; } int Producer_table_model::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) if ( track_list_) { return track_list_->count(); } else { return 0; } } int Producer_table_model::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) // // 序號、線程號、數(shù)據(jù)資源 // return 3; // 線程號、數(shù)據(jù)資源 return 2; } QVariant Producer_table_model::data(const QModelIndex &index, int role) const { if ( !index.isValid()) { return QVariant(); } QVariant v; if (Qt::DisplayRole == role) { switch (index.column()) { case 0: v = track_list_->at(index.row()).get_thread_id(); break; case 1: v = (QChar) track_list_->at(index.row()).get_char(); break; } return v; } return QVariant(); } QVariant Producer_table_model::headerData(int section, Qt::Orientation orientation, int role) const { if ((Qt::DisplayRole == role) && (Qt::Horizontal == orientation)) { QString header_content; switch (section) { case 0: header_content = tr("線程號"); break; case 1: header_content = tr("數(shù)據(jù)"); break; } return header_content; } return QAbstractTableModel::headerData(section, orientation, role); }
在這里特別注意PRODUCER_SPACE和CONSUMER_SPACE的初始值,因為資源BUFFER數(shù)組初始資源為0,所以生產(chǎn)者Producer可用的數(shù)組空間為所有,消耗著Consumer可用的數(shù)組空間為0,因為沒有資源可用。
運行結(jié)果如下圖:
感謝各位的閱讀,以上就是“怎么使用QSemaphore進(jìn)行多線程數(shù)據(jù)同步”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對怎么使用QSemaphore進(jìn)行多線程數(shù)據(jù)同步這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。