概述
代码段是希望被保护起来的,在执行过程中不被其他线程打断,以保证计算结果的完整性,这就是线程同步的概念。
基于互斥量的线程同步
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;
}