千家信息网

怎么使用QSemaphore进行多线程数据同步

发表于:2025-02-06 作者:千家信息网编辑
千家信息网最后更新 2025年02月06日,这篇文章主要讲解了"怎么使用QSemaphore进行多线程数据同步",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"怎么使用QSemaphore进行多线程
千家信息网最后更新 2025年02月06日怎么使用QSemaphore进行多线程数据同步

这篇文章主要讲解了"怎么使用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 /***************************************************************************  @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_SIZEchar BUFFER[BUFFER_SIZE];// 初始时可用资源位BUFFER_SIZEQSemaphore PRODUCER_SPACE(BUFFER_SIZE);QSemaphore CONSUMER_SPACE(0);

创建窗体:

Producer按钮生成资源,保存到数组中,在左侧文本框中显示Producer所在的线程及生成的资源。Consumer按钮消耗数组中的资源,在左侧文本框中显示Consumer所在的线程及消耗的资源。这两个按钮是单独运行。Both_start_PushButton按钮同时启动两个子线程,一个生产资源,一个消耗资源,生产和消耗的都在左边的文本框中显示。

窗口代码如下:

producer_consumer_dialog.ui 代码:

 Producer_consumer_dialog          0    0    722    451          Dialog                                                                        Producer                                                          Qt::Vertical                            QSizePolicy::Expanding                                       20           40                                                                                                                                        Consumer                                                          Qt::Vertical                                       20           40                                                                                                     Qt::Horizontal                               348         20                                                    Both_start_PushButton                              

producer_consumer_dialog.h 文件:

#ifndef PRODUCER_CONSUMER_DIALOG_H#define PRODUCER_CONSUMER_DIALOG_H#include #include QT_BEGIN_NAMESPACEclass Producer_move_to_thread;class Consumer_move_to_thread;class Producer_table_model;//class QCloseEvent;QT_END_NAMESPACEnamespace 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_OBJECTpublic:    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 /*****************************************************************************************  @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_OBJECTpublic:    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 #include #include #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 class Consumer_move_to_thread : public QObject{    Q_OBJECTpublic:    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 #include #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 /*****************************************************************************************  @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 #include #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_OBJECTpublic:    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* 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_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()){}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 *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进行多线程数据同步这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

0