Qt篇: 多线程同步

多线程的本质

并发性是多线程编程的本质。何为并发性?指在同一计算机中,存在多个计算任务,这些计算任务具有同时执行的特性,这样能提高系统性能,程序响应速度更快。

  1. 在宏观上,所有线程并行执行;
  2. 多个线程相互独立,互不干涉,那事实真是这样吗?

常规问题解决方案

在平常的问题解决方案中,往往都是将同一个问题分解成若干个任务去解决,这些任务在执行解决上,又可以分为串行执行和并发执行,两者在效率上是很大不同,并发性效率可能是串行的几倍。下面以0+1+2+3+ ... + 1000求和为例,可以将这个求和分为三部分:任务1为0-300,、任务2为301-600和任务3为601-1000来解决这个问题。

任务分解

串行执行及执行时间

#include <QCoreApplication>
#include <QThread>
#include <QTime>
#include <QDebug>
 
class SyncThread : public QThread
{
    int m_begin_data;
    int m_end_data;
    int m_result;
 
public:
    SyncThread(int begin, int end)
    {
        m_begin_data = begin;
        m_end_data = end;
        m_result = 0;
    }
 
    int getResult()
    {
        return m_result;
    }
 
    void work()
    {
        this->run();
    }
 
protected:
    void run()
    {
        qDebug() << objectName() << "run() begin...";
 
        for( int i=m_begin_data; i<=m_end_data; i++ )
        {
            m_result += i;
            msleep(10);
        }
 
        qDebug() << objectName() << "run() end...";
    }
};
 
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QTime timer;
    timer.start();
    qDebug() << "main begin...";
 
    SyncThread s1(0, 300);
    SyncThread s2(301, 600);
    SyncThread s3(601, 1000);
 
    s1.setObjectName("s1");
    s2.setObjectName("s2");
    s3.setObjectName("s3");
 
    s1.work();
    s2.work();
    s3.work();
 
    int result = s1.getResult() + s2.getResult() + s3.getResult();
    qDebug() << "result: " << result;
 
    qDebug() << "main end...";
    qDebug() << timer.elapsed() << " ms";
    return a.exec();
}
串行任务结果

并发执行及执行时间

    //s1.work();
    //s2.work();
    //s3.work();
    
    // 在串行代码上只需要调用相应的线程启动函数即可
    s1.start();
    s2.start();
    s3.start();
并行任务结果

结果分析

  • 现象:在计算耗时上,确实是缩短了好几倍,效率也提高上了,但是计算结果却是不准确的;
  • 原因:多线程间并不是完全独立毫无依赖的,在某些特殊情况下,多线程执行在时序上存在依赖。在main函数中,相继调用了三个线程去处理各自任务,但是三个线程并没有完全执行完整,计算结果就出乎我们意料了,这就是同步问题。

同步的概念和解决方法

  • 同步的概念:在特殊情况下,控制多线程间的相对执行顺序;
  • 解决方法:QThread类直接提供了相关函数接口解决该问题,就是wait()方法,wait()停止当前线程的执行,等待目标线程的执行结束;
QThread wait() API
  • 使用方法:按照下面的方式,依次给每条线程一个wait处理,即可得到正确的结果,且也证明耗时比串行的少,效率会比较高。
    // 在串行代码上只需要调用相应的线程启动函数即可
    s1.start();
    s2.start();
    s3.start();
 
    s1.wait();
    s2.wait();
    s3.wait();
QThread wait结果