序言

有一段时间在摸索多线程如何同步协成操作完成某一项任务,由于进入一个死胡同,导致自己进入没有头绪状态,整个脑回路了。然后今天把思路梳理下,整个人就明了了。废话不多说,开始介绍了。

线程

首先明白 线程 . 在Qt中,最直接的方式查看某个函数所在的线程,由QThread::currentThreadId()可以获得,它本身就是一个轻量级进程,有自己ID,以及相应的堆栈与寄存器,线程是进程的实体,是被系统独立调度和分派的基本单元。

多线程

多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理(Chip-level multithreading)或同时多线程(Simultaneous multithreading)处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理(Multithreading)”。

顾名思义就是多个线程同时进行操作,多个线程如果同时开启,那它们顺序是没法准确确定的,依据CPU所分配的时间段内以及相关资源在不同(执行,就绪,阻塞)等状态之间进行切换操作。因此如果要多线程协成操作,比如说可能会访问操作某个全局的数据,可能需要考虑到线程安全与是否可重入问题。

线程安全: 线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
线程安全问题大多是由全局变量及静态变量引起的,局部变量逃逸也可能导致线程安全问题。
百度百科线程安全理解
线程安全
Qt中常见的线程安全类: QMutex、QMutexLocker、QReadWriteLock、QReadLocker、QWriteLocker、QSemaphore、QThreadStorage以及QWaitCondition。

可重入: 就是可以重新再进入。就是在运行某个函数或者代码时因为某个原因(中断或者抢占资源问题)而中止函数或代码的运行,等到问题解决后,重新进入该函数或者代码继续运行。其结果不会受到影响(和没有被打断时,运行结果一样)。那么这个函数或者代码就称为可重入的。

可重入的函数必须满足以下三个条件:

(1)可以在执行的过程中可以被打断;

(2)被打断之后,在该函数一次调用执行完之前,可以再次被调用(或进入,reentered)。

(3)再次调用执行完之后,被打断的上次调用可以继续恢复执行,并正确执行。
所有的QWidget和他的子类都是不可重入的,所以qt中的界面类实例化对象只能在主线程中,也不要试图把界面类对象的所属线程更改到子线程中,更不要在子线程中直接操作界面类对象,要使用信号与槽技术或其他技术。
可重入
可重入

上述对可重入与线程安全进行说明。 现在我们继续说说如何多线程协作问题,如果控制不好,这样可能会出现线程不安全问题。可以通过QMutex、QMutexLocker、QReadWriteLock、QReadLocker、QWriteLocker、QSemaphore、QThreadStorage以及QWaitCondition。控制多线程同步操作。主要设计时考虑如下点:

  1. 考虑每次只有一个线程访问,其余要访问的线程需要处于等待阻塞。
  2. 如果多个线程同时启动started时,那他们顺序是随机的,不确定的,如果想要按照某个执行顺序去操作某个数据,可能需要去设置,然后激活下一个要执行线程。

代码

代码有点乱 ,只是提供思路参考。

#include <QMainWindow>
#include <iostream>
#include <QMutex>
#include <QWaitCondition>

class QThread;


class Data
{
public:
    volatile int num;
#if 0
    QMutex m_mutex;
    QMutex m_oneMutex;
    QMutex m_twoMutex;
    QMutex m_threeMutex;
    QWaitCondition m_waitConditionOne;
    QWaitCondition m_waitConditionTwo;
    QWaitCondition m_waitConditionThree;
#else
    QMutex m_mutex;
    volatile bool isAThread = true;
    volatile bool isBThread = false;
    volatile bool isCThread = false;
#endif
    static Data* m_pInstance;
public:
    static Data* getInstance()
    {
        if(nullptr ==  m_pInstance)
        {
            m_pInstance = new Data();
            m_pInstance->num = 0;
        }
        return m_pInstance;
    }
private:
    Data(){}
    ~Data(){}
};


namespace Ui {
class MainWindow;
}


class Worker: public QObject
{
Q_OBJECT
public:
    explicit Worker(QObject* parent = nullptr);
    ~Worker(){}

private slots:
    void onDoWorkOne();
private:

};

class WorkerTwo: public QObject
{
Q_OBJECT
public:
    explicit WorkerTwo(QObject* parent = nullptr);
    ~WorkerTwo(){}
private slots:
    void onDoWorkTwo();
private:

};

class WorkerThree: public QObject
{
Q_OBJECT
public:
    explicit WorkerThree(QObject* parent = nullptr);
    ~WorkerThree(){}

private slots:
    void onDoWorkThree();
private:

};

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
signals:
    void sigDoWork();
private:
    void init();
    void initThread();
private:
    Ui::MainWindow *ui;
    std::shared_ptr<QThread> m_pThreadOne;
    std::shared_ptr<QThread> m_pThreadTwo;
    std::shared_ptr<QThread> m_pThreadThree;
    std::shared_ptr<Worker> m_pWorker;
    std::shared_ptr<WorkerTwo> m_pWorkerTwo;
    std::shared_ptr<WorkerThree> m_pWorkerThree;
};
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
#include <QDebug>
#include <QMutexLocker>
Data* Data::m_pInstance = nullptr;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
  ,m_pWorker(nullptr)
{
    ui->setupUi(this);
    init();
    Data::getInstance();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::init()
{
    m_pWorker =  std::shared_ptr<Worker>(new Worker());
    m_pWorkerTwo =  std::shared_ptr<WorkerTwo>(new WorkerTwo());
    m_pWorkerThree =  std::shared_ptr<WorkerThree>(new WorkerThree());
    initThread();
    //启动线程
    m_pThreadOne.get()->start(QThread::HighestPriority);
    m_pThreadTwo.get()->start(QThread::HighPriority);
    m_pThreadThree.get()->start(QThread::NormalPriority);
    //调用moveToThread将该任务交给workThread
    m_pWorker.get()->moveToThread(m_pThreadOne.get());
    m_pWorkerTwo.get()->moveToThread(m_pThreadTwo.get());
    m_pWorkerThree.get()->moveToThread(m_pThreadThree.get());
    //将信号与槽连接
    connect(this, SIGNAL(sigDoWork()), m_pWorker.get(), SLOT(onDoWorkOne()));
    connect(this, SIGNAL(sigDoWork()), m_pWorkerTwo.get(), SLOT(onDoWorkTwo()));
    connect(this, SIGNAL(sigDoWork()), m_pWorkerThree.get(), SLOT(onDoWorkThree()));

    emit sigDoWork();

//    m_pWorkerTwo.get()->sleepThread();
//    m_pWorkerThree.get()->sleepThread();
}

void MainWindow::initThread()
{
    m_pThreadOne = std::shared_ptr<QThread>(new QThread());
    m_pThreadTwo = std::shared_ptr<QThread>(new QThread());
    m_pThreadThree= std::shared_ptr<QThread>(new QThread());
}

Worker::Worker(QObject *parent)
    :QObject (parent)
{

}

void Worker::onDoWorkOne()
{
    while(1)
    {
//        Data::getInstance()->m_mutex.lock();
//        Data::getInstance()->m_twoMutex.lock();
//        Data::getInstance()->m_waitConditionTwo.wakeOne();
//        Data::getInstance()->m_twoMutex.unlock();


//        Data::getInstance()->m_oneMutex.lock();
//        Data::getInstance()->m_waitConditionTwo.wakeOne();
//        Data::getInstance()->m_waitConditionOne.wait(&Data::getInstance()->m_oneMutex);
//        ++Data::getInstance()->num;
//        qDebug()<<__FUNCTION__<<"current thread ID:"<<QThread::currentThreadId()<< " cur num = "<<Data::getInstance()->num;
//        Data::getInstance()->m_oneMutex.unlock();
        QMutexLocker locker(&Data::getInstance()->m_mutex);
        if(Data::getInstance()->isAThread)
        {
            ++Data::getInstance()->num;
            qDebug()<<__FUNCTION__<<"current thread ID:"<<QThread::currentThreadId()<< " cur num = "<<Data::getInstance()->num;
            Data::getInstance()->isAThread = false;
            Data::getInstance()->isBThread = true;
        }
    }
}

WorkerTwo::WorkerTwo(QObject *parent)
    :QObject (parent)
{

}

void WorkerTwo::onDoWorkTwo()
{
    while(1)
    {
//        Data::getInstance()->m_threeMutex.lock();
//        Data::getInstance()->m_waitConditionThree.wakeOne();
//        Data::getInstance()->m_threeMutex.unlock();

//        Data::getInstance()->m_twoMutex.lock();
//        Data::getInstance()->m_waitConditionThree.wakeOne();
//        Data::getInstance()->m_waitConditionTwo.wait(&Data::getInstance()->m_twoMutex);
//        ++Data::getInstance()->num;
//        qDebug()<<__FUNCTION__<<"current thread ID:"<<QThread::currentThreadId()<< " cur num = "<<Data::getInstance()->num;
//        Data::getInstance()->m_twoMutex.unlock();

        QMutexLocker locker(&Data::getInstance()->m_mutex);
        if(Data::getInstance()->isBThread)
        {
            ++Data::getInstance()->num;
            qDebug()<<__FUNCTION__<<"current thread ID:"<<QThread::currentThreadId()<< " cur num = "<<Data::getInstance()->num;
            Data::getInstance()->isBThread = false;
            Data::getInstance()->isCThread = true;
        }
    }
}

WorkerThree::WorkerThree(QObject *parent)
    :QObject (parent)
{

}

void WorkerThree::onDoWorkThree()
{
    while(1)
    {
//        Data::getInstance()->m_oneMutex.lock();
//        Data::getInstance()->m_waitConditionOne.wakeOne();
//        Data::getInstance()->m_oneMutex.unlock();

//        Data::getInstance()->m_threeMutex.lock();
//        Data::getInstance()->m_waitConditionOne.wakeOne();
//        Data::getInstance()->m_waitConditionThree.wait(&Data::getInstance()->m_threeMutex);
//        ++Data::getInstance()->num;
//        qDebug()<<__FUNCTION__<<"current thread ID:"<<QThread::currentThreadId()<< " cur num = "<<Data::getInstance()->num;
//        Data::getInstance()->m_threeMutex.unlock();
        QMutexLocker locker(&Data::getInstance()->m_mutex);
        if(Data::getInstance()->isCThread)
        {
            ++Data::getInstance()->num;
            qDebug()<<__FUNCTION__<<"current thread ID:"<<QThread::currentThreadId()<< " cur num = "<<Data::getInstance()->num;
            Data::getInstance()->isCThread = false;
            Data::getInstance()->isAThread = true;
        }
    }
}

结果图:
在这里插入图片描述
在这里插入图片描述

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐