概述

代码段是希望被保护起来的,在执行过程中不被其他线程打断,以保证计算结果的完整性,这就是线程同步的概念。

基于互斥量的线程同步

QMutex

  • lock():锁定互斥量,如果另外一个线程锁定了这个互斥量,它将阻塞执行直到其他线程解锁这个互斥量;
  • unlock():解锁一个互斥量,需要与lock()配对使用;
  • tryLock():视图锁定一个互斥量,如果成功锁定就返回true,如果其他线程已经锁定了这个互斥量,就返回false,但不阻塞程序执行;

QMutexLocker
QMutexLocker是另外一个简化互斥量处理的类,QMutexLocker的构造函数接受一个互斥量作为参数并将其锁定,QMutexLocker的析构函数则将此互斥量解锁,所以在QMutexLocker实例变量的生存期内的代码段得到保护,自动进行互斥量的锁定和解锁;

QMutex mutex;
QMutexLocker Locker(&mutex);

基于QReadWriteLock的线程同步

使用互斥量存在一个问题,每次只能有一个线程获得互斥量的权限,如果在一个程序中有多个线程读取某个变量,使用互斥量时也必须排队,而实际上若只是读取一个变, 是可以让多个线程同时访问的,这样互斥量就会降低程序的性能。
它是基于读或写的模式进行代码段锁定的,在多个线程读写一个共享数据时,可以解决上面所说的互斥量存在的问题,QReadWriteLock以读或写锁定的同步方法允许以读或写的方式保护一段代码,它可以允许多个线程以只读方式同步访问资源,但是只要有一个线程在以写方式访问资源时,其他线程就必须等待直到写操作结束。
QReadLocker和QWriteLocker是QReadWriteLock简便形式。

主要函数

  • lockForRead():以只读的方式锁定资源,如果有其他线程以写入方式锁定,这个函数会阻塞;
  • lockForWrite():以写入方式锁定资源,如果本线程或其他线程以读或写模式锁定资源,这个函数就阻塞;
  • unlock():解锁;
  • tryLockForRead():是lockForRead()非阻塞版本;
  • tryLockForWrite():是lockForWrite()非阻塞版本;

代码示例
ReadWriteThread.h

#ifndef READWRITETHREAD_H
#define READWRITETHREAD_H

#include <QObject>
#include <QThread>
#include <QReadWriteLock>
#include <QReadLocker>
#include <QWriteLocker>

class ReadWriteThread : public QThread
{
    Q_OBJECT
public:
    ReadWriteThread();

    void run() Q_DECL_OVERRIDE;

    QReadWriteLock lock;
};

#endif // READWRITETHREAD_H

ReadWriteThread.cpp

#include "ReadWriteThread.h"

ReadWriteThread::ReadWriteThread() {}

// void ReadWriteThread::run()
// {
//     QWriteLocker lockW(&lock);
//     lock.lockForWrite();
//     //模拟线程1数据写入代码
//     //。。。
//     lock.unlock();
// }


// void ReadWriteThread::run()
// {
//     QReadLocker lockR(&lock);
//     lock.lockForRead();
//     //模拟线程2读取数据代码
//     //。。。
//     lock.unlock();
// }



void ReadWriteThread::run()
{
    //QReadLocker lockR(&lock);
    lock.lockForRead();
    //模拟线程3读取数据代码
    //。。。
    lock.unlock();
}

基于QWaitCondition的线程同步

QWaitCondition提供了另外一种改进的线程同步方法,QWaitCondition与QMutex结合,可以使一个线程在满足一定条件时通知其他多个线程,使它们及时做出响应,这样比只使用互斥量效率要高一些。
QWaitCondition一般用于生产者/消费者模型中,生产者产生数据,消费者消费数据。
主要函数

  • wait(QMutex *lockedMutex, unsigned long time):解锁互斥量lockedMutex,并阻塞等待唤醒条件,被唤醒后锁定lockedMutex并退出函数;
  • wakeAll():唤醒所有处理等待状态的线程,线程唤醒的顺序不确定,由操作系统的调度策略决定;
  • wakeOne():唤醒一个处理等待状态的线程,唤醒哪个线程不确定,由操作系统的调度策略决定;
    代码示例
    QMyThread.h
#ifndef QMYTHREAD_H
#define QMYTHREAD_H
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
class QMyThread : public QThread
{

};

class QThreadProducer : public QThread
{

    Q_OBJECT

public:
    QThreadProducer();
    void run() Q_DECL_OVERRIDE;
    void stopThread();

    bool m_stop=false;

};

class QThreadConsumer : public QThread
{
    Q_OBJECT
public:
    QThreadConsumer();
    void run() Q_DECL_OVERRIDE;

    void stopThread();
    bool m_stop=false;
signals:
    void newValue(int seq, int diceValue);
};




#endif // QMYTHREAD_H

QMyThread.cpp

#include "QMyThread.h"
#include <QTime>
int seq = 0;
int diceValue = 0;

QMutex mutex;
QWaitCondition newdataAvailable;

QThreadProducer::QThreadProducer() {}

void QThreadProducer::run()
{
    m_stop= false;
    seq = 0;
    qsrand(QTime::currentTime().msec());
    while(!m_stop) {
        mutex.lock();
        diceValue=qrand();
        diceValue = (diceValue % 6)+1;
        seq++;
        mutex.unlock();
        newdataAvailable.wakeAll();//唤醒所有线程
        msleep(500);
    }
}

void QThreadProducer::stopThread()
{
    m_stop = true;
}

QThreadConsumer::QThreadConsumer() {}


void QThreadConsumer::run()
{
    m_stop= false;
    while(!m_stop) {
        mutex.lock();
        newdataAvailable.wait(&mutex);//先解锁mutex,使其他线程可以使用mutex
        emit newValue(seq, diceValue);
        mutex.unlock();
    }
}

void QThreadConsumer::stopThread()
{
    m_stop = true;
}

基于信号量的线程同步

信号量(Semaphore)是另一种限制对共享资源进行访问的线程同步机制,它与互斥量(Mutex)相似,但是有区别,一个互斥量只能被锁一次,而信号量可以多次使用,信号量通常用来保护一定数量的相同的资源,如数据采集时的双缓冲区。

主要函数

  • acquire(int n = 1):尝试获取n个资源,如果没有这么多资源,线程将阻塞直到有n个资源可用;
  • release(int n = 1):释放n个资源,如果信号量的资源已全部可用之后在release(),就可以创建更多的资源,增加可用资源的个数;
  • available() const:返回当前信号量可用的资源个数,这个数用于不可能为负数,如果为0,说明当前没有资源可用;
  • tryAcquire(int n = 1):尝试获取n个资源,不成功时不阻塞线程;

代码示例
SemaphoreThread.h

#ifndef SEMAPHORETHREAD_H
#define SEMAPHORETHREAD_H
#include <QThread>

class SemaphoreThread
{

};


class QThreadDao : public QThread
{
    Q_OBJECT
public :
    QThreadDao();
    bool m_stop = false;

    void run() Q_DECL_OVERRIDE;

    void stopThread();

};

class QThreadShow : public QThread
{

    Q_OBJECT
public :
    QThreadShow();
    bool m_stop = false;

    void run() Q_DECL_OVERRIDE;

    void stopThread();


signals:
    void newValue(int* data, int count, int seq);

};

#endif // SEMAPHORETHREAD_H

SemaphoreThread.cpp

#include "SemaphoreThread.h"
#include <QSemaphore>

const int BufferSize = 8;
int buffer1[BufferSize];
int buffer2[BufferSize];

int curBuf =1;
int bufNo =0;
quint8 counter = 0;

QSemaphore emptyBufs(2);//信号量,空的缓冲区个数,初始资源格式为2
QSemaphore fullBufs;//满的缓冲区个数,初始资源为0

QThreadDao::QThreadDao()
{
}

void QThreadDao::run()
{
    m_stop = false;
    bufNo = 0;//缓冲区序号
    curBuf =1;//当前写入使用的缓冲区
    counter = 0;//数据生成器
    int n = emptyBufs.available();
    if(n<2) {
        //保证线程启动的时候,emptyBufs.available =2
        emptyBufs.release(2-n);
    }
    while(!m_stop) {
        emptyBufs.acquire();//获取一个空的缓冲区
        for(int i=0; i<BufferSize; i++) {
            if(curBuf == 1) {
                buffer1[i] = counter;//向缓冲区写入数据
            } else {
                buffer2[i] = counter;//向缓冲区写入数据
            }
            counter++;
            msleep(50);
        }
        bufNo++;
        //切换当前写入缓冲区
        if(curBuf == 1) {
            curBuf =2;
        } else {
            curBuf =1;
        }
        fullBufs.release();
    }
}

void QThreadDao::stopThread()
{
    m_stop = true;
}

QThreadShow::QThreadShow()
{
}

void QThreadShow::run()
{
    m_stop = false;
    int n = fullBufs.available();
    if(n>0) {
        fullBufs.acquire(n);
    }
    while(!m_stop) {
        fullBufs.acquire();//等待有缓冲区满当 fullBufs.available()=0 阻塞
        int bufferData[BufferSize];
        int seq = bufNo;
        if(curBuf==1) {
            //当前写入缓冲区是1,那么满的缓冲区是2
            for(int i=0; i<BufferSize; i++) {
                bufferData[i] = buffer2[i];
            }
        } else {
            for(int i=0; i<BufferSize; i++) {
                bufferData[i] = buffer1[i];
            }
        }
        emptyBufs.release();//释放一个空缓冲区
        emit newValue(bufferData, BufferSize, seq);
    }
}

void QThreadShow::stopThread()
{
    m_stop = true;
}

03-16 13:12