这篇文章主要讲解了“怎么使用QSemaphore进行多线程数据同步”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么使用QSemaphore进行多线程数据同步”吧!
20210127:在生产者、消费者的方法中添加线程挂起方法QThread::usleep(10),使ui不卡。
20210128:在添加Track类(保存生产者Producer生成的每组数据),在ui界面中使用model-view同步显示生产者生成的数据,model-view不会对主线程造成卡顿。对消费者同样创建view,还没有进行model绑定。
避免引起主线程的阻塞,Qt在子线程中处理大数据,当多个子线程需要处理同一块数据时,需要使用数据同步,避免出现调用错乱情况,在这里我们在两个子线程使用QSemaphore作为标志位,对数组进行标识,生产者线程将生成的资源存入数组,消费者数组消耗数组内的资源,当有一方的速度过快导致数组资源耗尽时,该子线程被阻塞,直到有资源时子线程继续。代码如下:
在全局变量中声明数组、数组大小、资源总量:
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 设置循环保存数据的数组大小,相当于设置“缓存”大小 ***************************************************************************/ extern const int BUFFER_SIZE; /*************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @date 2021-01-24 @brief 要读取的总资源数量,数据量大,无法一次读取完,需要长时间,分批读取 ***************************************************************************/ extern const int DATA_SIZE; /*************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @date 2021-01-24 @brief 循环使用,保存数据资源的数组 ***************************************************************************/ extern char BUFFER[]; /*************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @date 2021-01-24 @brief 标识Producer可以保存资源的空余位置的数量,保存在数组中的数据量,因为初 始状态数组中没有任何数据,数组有DATA_SIZE个资源可用 ***************************************************************************/ extern QSemaphore PRODUCER_SPACE; /*************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @date 2021-01-24 @brief 标识Consumer可以使用的已保存资源的数量,因为初始时没有数据可供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); // 定义数组大小为BUFFER_SIZE char BUFFER[BUFFER_SIZE]; // 初始时可用资源位BUFFER_SIZE QSemaphore PRODUCER_SPACE(BUFFER_SIZE); QSemaphore CONSUMER_SPACE(0);
创建窗体:
Producer按钮生成资源,保存到数组中,在左侧文本框中显示Producer所在的线程及生成的资源。Consumer按钮消耗数组中的资源,在左侧文本框中显示Consumer所在的线程及消耗的资源。这两个按钮是单独运行。Both_start_PushButton按钮同时启动两个子线程,一个生产资源,一个消耗资源,生产和消耗的都在左边的文本框中显示。
窗口代码如下:
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 创建主窗口,生成创造者producer,并在子线程-1生产资源。生成消费者consumer,并在子线程-2消耗生 产资源。资源均存储在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设置到文本框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设置到文本框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 消费者产生数据对应的模型 ***************************************************************************/ 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); }
生产类
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 生产者,会被移动到子线程中产生资源 ******************************************************************************************/ 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) { // 生产1个资源(总共要产生的DATA_SZIE个资源),然后保存到BUFFER数组,BUFFER数组空余位置少1个 PRODUCER_SPACE.acquire(1); // 计算获取资源序号、线程号、数据资源 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; // 将数据传给窗体Producer_consumer_dialog // emit signal_string(serial_number, thread_id, ch); emit signal_index_id_content(serial_number, thread_id, ch); // 线程挂起,不对主线程造成卡顿 QThread::usleep(10); // 数组中已保存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数组中取出,BUFFER数组空余位置多1个。当 // 没有资源提供时,进程被挂起,待有足够资源后再执行后续程序 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); // 使用数组中1个资源,数组释放1个空间,给生产者提供1个空余位置 PRODUCER_SPACE.release(1); } }
保存生产者生成的数据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 数据类,保存随机生成的资源 ******************************************************************************************/ class Track { public: /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-28 @brief 构造函数 @param index 数据的生成序列号 @param thread_id 生成数据的线程号 @param ch 生成的资源数据 ***************************************************************************/ 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 拷贝构造函数 ***************************************************************************/ 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_; }
生产者模型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) // // 序号、线程号、数据资源 // return 3; // 线程号、数据资源 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("数据"); break; } return header_content; } return QAbstractTableModel::headerData(section, orientation, role); }
在这里特别注意PRODUCER_SPACE和CONSUMER_SPACE的初始值,因为资源BUFFER数组初始资源为0,所以生产者Producer可用的数组空间为所有,消耗着Consumer可用的数组空间为0,因为没有资源可用。
运行结果如下图:
感谢各位的阅读,以上就是“怎么使用QSemaphore进行多线程数据同步”的内容了,经过本文的学习后,相信大家对怎么使用QSemaphore进行多线程数据同步这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。