一、多线程程序
QThread类提供了管理线程的方法:
- 一个对象管理一个线程
- 一般从QThread继承一个自定义类,重载run函数
1、实现程序
(1)创建项目,基于QDialog
(2)添加类,修改基于QThread
bash
复制代码
#ifndef DICETHREAD_H
#define DICETHREAD_H
#include <QThread>
class DiceThread : public QThread
{
Q_OBJECT
private:
int m_seq = 0;
int m_diceValue;
bool m_Paused = true;
bool m_stop = false;
public:
explicit DiceThread();
void diceBegin();
void dicePause();
void stopThread();
protected:
void run() Q_DECL_OVERRIDE;
signals:
void newValued(int seq, int diceValue);
public slots:
};
#endif // DICETHREAD_H
bash
复制代码
#include "dicethread.h"
#include <QTime>
DiceThread::DiceThread()
{
}
void DiceThread::diceBegin()
{
m_Paused = false;
}
void DiceThread::dicePause()
{
m_Paused = true;
}
void DiceThread::stopThread()
{
m_stop = true;
}
void DiceThread::run()
{
m_stop = false;
m_seq = 0;
qsrand(QTime::currentTime().second());
while (!m_stop) {
if(!m_Paused)
{
m_diceValue = qrand()%6+1;
m_seq++;
emit newValued(m_seq, m_diceValue);
}
sleep(1);
}
quit();
}
(3)实现按钮功能
bash
复制代码
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
ui->btnStartThread->setEnabled(true);
ui->btnStart->setEnabled(false);
ui->btnStop->setEnabled(false);
ui->btnStopThread->setEnabled(false);
connect(&threadA, SIGNAL(started()),
this, SLOT(on_threadAStarted()));
connect(&threadA, SIGNAL(finished()),
this, SLOT(on_threadAFinished()));
connect(&threadA, SIGNAL(newValued(int,int)),
this, SLOT(on_threadAnewValue(int,int)));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::closeEvent(QCloseEvent *event)
{
if(threadA.isRunning())
{
threadA.stopThread();
threadA.wait();
}
event->accept();
}
void Dialog::on_btnStartThread_clicked()
{
threadA.start();
}
void Dialog::on_btnStart_clicked()
{
threadA.diceBegin();
}
void Dialog::on_btnStop_clicked()
{
threadA.dicePause();
}
void Dialog::on_btnStopThread_clicked()
{
threadA.stopThread();
}
void Dialog::on_btnClearText_clicked()
{
ui->plainTextEdit->clear();
}
void Dialog::on_threadAnewValue(int seq, int diceValue)
{
ui->plainTextEdit->appendPlainText(QString::asprintf("第%d次投色子: 点数%d", seq, diceValue));
}
void Dialog::on_threadAStarted()
{
ui->labelStatus->setText("Thread状态:started");
ui->btnStartThread->setEnabled(false);
ui->btnStart->setEnabled(true);
ui->btnStop->setEnabled(true);
ui->btnStopThread->setEnabled(true);
}
void Dialog::on_threadAFinished()
{
ui->labelStatus->setText("Thread状态:finished");
ui->btnStartThread->setEnabled(true);
ui->btnStart->setEnabled(false);
ui->btnStop->setEnabled(false);
ui->btnStopThread->setEnabled(false);
}
二、互斥量
QMutex和QMutexLocker是基于互斥量的线程同步类
- QMutex定义的实力是互斥量,主要提供了三个函数
- lock():锁定互斥量,如果另一个线程锁定了这个互斥量,将阻塞直到另一个解锁
- unlock():解锁一个互斥量
- trylock():尝试锁定一个互斥量,如果成功返回true,失败(其他线程已经锁定这个互斥量)返回false,不阻塞线程。
- QMutexLocker简化了互斥量的处理
- 构造一个函数接受一个互斥量作为参数,并将其锁定
- 析构函数解锁该互斥量
1、实现程序
(1)拷贝上一个项目
(2)修改程序为直接读取
bash
复制代码
void DiceThread::readValue(int *seq, int *diceValue)
{
*seq = m_seq;
*diceValue = m_diceValue;
}
void DiceThread::run()
{
m_stop = false;
m_seq = 0;
qsrand(QTime::currentTime().second());
while (!m_stop) {
if(!m_Paused)
{
m_diceValue = 50;
msleep(50);
m_diceValue = qrand();
msleep(50);
m_diceValue = m_diceValue%6+1;
msleep(50);
m_seq++;
// emit newValued(m_seq, m_diceValue);
}
sleep(1);
}
quit();
}
bash
复制代码
void Dialog::on_TimerOut()
{
int seq, diceValue;
threadA.readValue(&seq, &diceValue);
ui->plainTextEdit->appendPlainText(QString::asprintf("第%d次投色子: 点数%d", seq, diceValue));
}
(3)使用QMutex互斥量
bash
复制代码
void DiceThread::readValue(int *seq, int *diceValue)
{
mMutex.lock();
*seq = m_seq;
*diceValue = m_diceValue;
mMutex.unlock();
}
void DiceThread::run()
{
m_stop = false;
m_seq = 0;
qsrand(QTime::currentTime().second());
while (!m_stop)
{
if(!m_Paused)
{
mMutex.lock();
m_diceValue = 50;
msleep(50);
m_diceValue = qrand();
msleep(50);
m_diceValue = m_diceValue % 6 + 1;
msleep(50);
m_seq++;
// emit newValued(m_seq, m_diceValue);
mMutex.unlock();
}
sleep(1);
}
quit();
}
(4)使用QMutexLocker
bash
复制代码
void DiceThread::readValue(int *seq, int *diceValue)
{
QMutexLocker locker(&mMutex);
*seq = m_seq;
*diceValue = m_diceValue;
}
(5)使用QMutex.trylock
bash
复制代码
bool DiceThread::readValue(int *seq, int *diceValue)
{
// QMutexLocker locker(&mMutex);
if(mMutex.tryLock())
{
*seq = m_seq;
*diceValue = m_diceValue;
mMutex.unlock();
return true;
}
return false;
}
三、读写锁
QReadWriteLock提供了以下主要函数:
- lockForRead():只读方式锁定资源,如果有其他线程以写入方式锁定,这个函数会阻塞
- lockForWrite():以写入方式锁定资源,如果本线程或者其他线程以读取或写入锁定资源,则函数阻塞
- unlock():解锁
- tryLockForRead():是lockForRead非阻塞版本
- tryLockForWrite():是lockForWrite非阻塞版本
- 读写锁同样有QReadLocker和QWriteLocker
四、条件变量QWaitCondition
QWaitCondition用于通知其他线程,如接收数据和处理数据之间通知。提供了一些函数:
- wait(QMutex *lockedMutex):进入等待状态,解锁互斥量lockMutex,被唤醒后锁定lockMutex并退出函数
- wakeAll():唤醒所有处于等待的线程,线程唤醒的顺序不确定,有操作系统调度策略决定
- QakeOne():唤醒一个处于等待状态的线程,唤醒哪个线程不确定,由操作系统调度策略决定
1、实现程序
(1)拷贝上一个项目
(2)使用QWaitCondition设置数据更新
bash
复制代码
#include "dicethread.h"
#include <QTime>
#include <QWaitCondition>
#include <QMutex>
int m_seq = 0;
int m_diceValue;
bool m_stop = false;
QMutex m_Mutex;
QWaitCondition waitCondition;
ProducerThread::ProducerThread()
{
}
void ProducerThread::stopThread()
{
m_stop = true;
}
void ProducerThread::run()
{
m_stop = false;
m_seq = 0;
qsrand(QTime::currentTime().second());
while (!m_stop)
{
m_Mutex.lock();
m_diceValue = qrand() % 6 + 1;
m_seq++;
m_Mutex.unlock();
waitCondition.wakeOne();
sleep(1);
}
quit();
}
ConsumerThread::ConsumerThread()
{
}
void ConsumerThread::stopThread()
{
m_stop = true;
waitCondition.wakeOne(); // 需要给wait置信号,否则阻塞无法结束
}
void ConsumerThread::run()
{
m_stop = false;
while (!m_stop)
{
m_Mutex.lock();
waitCondition.wait(&m_Mutex);
emit newValued(m_seq, m_diceValue);
m_Mutex.unlock();
msleep(100);
}
quit();
}
五、信号量
QSemaphore信号量通常用于保护一定数量的相同的资源。QSemaphore是实现信号量功能的类,提供了以下函数:
- acquire(int n):尝试获得n个资源,如果不够将阻塞线程,直到n个资源可用
- release(int n):释放资源,如果资源已经全部可用,则可扩充资源总数
- int available():返回房前信号量的资源个数
- bool tryAcquire(int n=1):尝试获取n个资源,不成功是,不阻塞线程
1、实现程序
(1)创建项目,基于QDIalog
(2)创建线程类
(3)使用信号量实现功能
bash
复制代码
#include "threadtest.h"
#include <QSemaphore>
const int bufferSize = 8;
int buffer1[bufferSize] = {0};
int buffer2[bufferSize] = {0};
int curBuf = 1; // 当前采集数据使用的缓冲区
QSemaphore semEmptyBufs(2); // 两个资源
QSemaphore semFullBufs;
ThreadDAQ::ThreadDAQ()
{
}
void ThreadDAQ::stopThread()
{
m_stop = true;
}
void ThreadDAQ::run()
{
m_stop = false;
int counter = 0;
while(!m_stop)
{
semEmptyBufs.acquire();
for (int i = 0; i < bufferSize; ++i)
{
if(curBuf == 1)
{
buffer1[i] = counter;
}
else
{
buffer2[i] = counter;
}
counter++;
msleep(50);
}
if(curBuf == 1)
{
curBuf = 2;
}
else
{
curBuf = 1;
}
semFullBufs.release();
}
exit();
}
ThreadShow::ThreadShow()
{
}
void ThreadShow::stopThread()
{
m_stop = true;
}
void ThreadShow::run()
{
m_stop = false;
int seq = 0;
while(!m_stop)
{
semFullBufs.acquire();
int buf[bufferSize] = {0};
if(curBuf == 1)
{
memcpy(buf, buffer2, sizeof(int)*bufferSize);
}
else
{
memcpy(buf, buffer1, sizeof(int)*bufferSize);
}
emit newValue(buf, bufferSize, seq++);
semEmptyBufs.release();
}
exit();
}
bash
复制代码
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
ui->btnStopThread->setEnabled(false);
connect(&threadConsumer, SIGNAL(newValue(int*, int, int)),
this, SLOT(on_threadNewValue(int*, int, int)));
connect(&threadProducer, SIGNAL(started()),
this, SLOT(on_threadProducer_started()));
connect(&threadProducer, SIGNAL(finished()),
this, SLOT(on_threadProducer_finished()));
connect(&threadConsumer, SIGNAL(started()),
this, SLOT(on_threadConsumer_started()));
connect(&threadConsumer, SIGNAL(finished()),
this, SLOT(on_threadConsumer_finished()));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::on_threadNewValue(int *data, int count, int seq)
{
QString str = QString::asprintf("第%03d次,内容:", seq);
for (int var = 0; var < count; ++var)
{
str += QString::asprintf("%03d ,", data[var]);
}
ui->plainTextEdit->appendPlainText(str);
}
void Dialog::on_btnStartThread_clicked()
{
threadConsumer.start();
threadProducer.start();
ui->btnStartThread->setEnabled(false);
ui->btnStopThread->setEnabled(true);
}
void Dialog::on_btnStopThread_clicked()
{
threadProducer.stopThread();
threadConsumer.stopThread();
ui->btnStartThread->setEnabled(true);
ui->btnStopThread->setEnabled(false);
}
void Dialog::on_btnClearText_clicked()
{
ui->plainTextEdit->clear();
}
void Dialog::on_threadProducer_started()
{
ui->labelProducer->setText("Producer线程:started");
}
void Dialog::on_threadProducer_finished()
{
ui->labelProducer->setText("Producer线程:finished");
}
void Dialog::on_threadConsumer_started()
{
ui->labelConsumer->setText("Consumer线程:started");
}
void Dialog::on_threadConsumer_finished()
{
ui->labelConsumer->setText("Consumer线程:finished");
}