Qt的线程是跨平台的,所以一份代码可以调用不同平台上的底层线程对象。
一、线程基础知识(在初学C++的时候相信已经学过了,而且C++现在也有自己的线程类了,所以略过)
二、在Qt中如何使用线程。
Qt自身也在很多地方用到了线程,比如信号和槽,事件的分发,计时器,事件的循环等等。
主题:
1、线程涉及到的类,先来个大概了解
属于Qt并发模块,用于线程的高级编程,比如处理高并发(服务器百万千万级别的并发访问等等) | |
QAtomicInteger |
对整数的独立于平台的原子操作 |
QAtomicPointer |
对指针的独立于平台的原子操作 |
QFuture |
表示异步计算的结果 |
QFutureSynchronizer |
QFutureSynchronizer |
QFutureWatcher |
允许使用信号和插槽监视QFuture。 |
QMutex |
互斥锁,用于锁住线程 |
QMutexLocker |
是一个方便的类,它简化了互斥锁和解锁互斥锁 |
QReadLocker |
一个方便的类,它简化了读写锁的锁定和解锁。 |
QReadWriteLock |
读写锁 |
QWriteLocker |
一个方便的类,它简化了读写访问锁的锁定和解锁。 |
QRunnable |
是一个接口,用于表示需要执行的任务或代码片段,由run()函数的重新实现表示。类似于java中的Runnable |
QSemaphore |
信号量 |
QSemaphoreReleaser |
构造QSemaphoreReleaser会延迟对信号量的release()调用,直到QSemaphoreReleaser被销毁 |
QThread |
线程对象 |
QThreadPool |
线程池 |
QThreadStorage |
一个模板类,它提供每个线程的数据存储。 |
QWaitCondition |
条件变量 |
2、Qt可以用来实现多线程的方式(重点)
<1>直接实例化QThread,或者子类化QThread
<2>使用线程池QThreadPool。要在QThreadPool的线程中运行代码,请重新实现QRunnable::run()并实例化子类QRunnable。
<3>Qt Concurrent并发模块提供高级函数,处理一些常见的并行计算模式:映射、筛选和缩减。
<4>在QML 的JavaScript代码中使用WorkerScript
Qt贴心的给出了这四种方式的应用场景(就差帮你写代码了+_+):
线程的生命周期 | 线程主要用来干嘛 | 推荐的编码逻辑设计 |
只 调用一次 (实例1) |
在另一个线程中运行一个新的线性函数,可以在运行期间进行进度更新。 | 1、将函数放在QThread::run()的重新实现中,并启动QThread。发出更新进度的信号。 2、将函数放在QRunnable::run()的重新实现中,并将QRunnable添加到QThreadPool中。写入线程安全变量以更新进程。 3、使用QtConcurrent:: Run()运行函数。写入线程安全变量以更新进程。 |
只调用一次 (实例2) |
在另一个线程中运行现有的函数并获取其返回值。 | 使用QtConcurrent:: Run()运行函数。让QFutureWatcher在函数返回时发出finish()信号,并调用QFutureWatcher::result()来获取函数的返回值。 |
只调用一次 (实例3) |
使用所有可用的内核对容器的所有项执行操作。例如,从图像列表生成缩略图。 | 使用QtConcurrent的QtConcurrent::filter()函数选择容器元素,使用QtConcurrent::map()函数对每个元素应用一个操作。 要将输出折叠成单个结果,可以使用QtConcurrent::filteredReduced()和QtConcurrent::mappedReduced()。 |
只调用一次/永久存在 (实例4) |
在纯QML应用程序中执行长时间的计算,并在结果准备好时更新GUI。 | 将计算代码放在.js脚本中,并将其附加到WorkerScript实例中。 调用sendMessage()在新线程中启动计算。让脚本也调用WorkerScript::sendMessage(),将结果传递回GUI线程。 处理onMessage中的结果并更新那里的GUI。 |
永久存在 (实例5) |
让一个对象驻留在另一个线程中,该线程可以根据请求和/或接收要处理的新数据来执行不同的任务。 | 1、子类化QObject以创建worker类。 2、实例化这个worker对象和一个QThread。 3、将worker移动到新线程,使用moveToThread方法。 4、与worker绑定信号和槽,通过信号或者槽来让worker工作 |
永久存在 (实例6) |
在另一个线程中重复执行耗时的操作,该线程不需要接收任何信号或事件。 | 直接在QThread::run()的重新实现中编写无限循环。在没有事件循环的情况下启动线程。让线程发出信号将数据发送回GUI线程。 |
os:膜拜Qt了。
实例1:
#include <QApplication>
#include <QProgressDialog>
#include <QThread>
#include <QRunnable>
#include <QThreadPool>
#include <QDebug>
class DownLoadThread : public QThread
{
Q_OBJECT
protected:
void run()
{
for(int i = 0; i <= 100; ++i){
msleep(100);//假装在下载
emit updateProcess(i);
}
}
signals:
void updateProcess(int value);
};
class DownLoadRunnable : public QRunnable
{
public:
DownLoadRunnable(QProgressDialog *dialog)
{
m_dialog = dialog;
}
~DownLoadRunnable()
{
qDebug() << "~DownLoadRunnable";
}
protected:
void run()
{
for(int i = 0; i <= 100; ++i){
QThread::msleep(100);//假装在下载
QMetaObject::invokeMethod(m_dialog, "setValue", Q_ARG(int, i));
}
}
private:
QProgressDialog *m_dialog;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QProgressDialog dialog;
dialog.setCancelButton(0);
dialog.setAutoReset(false);
dialog.setMinimum(0);
dialog.setMaximum(100);
dialog.show();
// DownLoadThread thread;
// QObject::connect(&thread,&DownLoadThread::updateProcess,&dialog,&QProgressDialog::setValue);
// thread.start();
DownLoadRunnable *runnbale = new DownLoadRunnable(&dialog);
runnbale->setAutoDelete(true);
QThreadPool::globalInstance()->start(runnbale);
return a.exec();
}
#include "main.moc"
实例2:
int getProcess()
{
QThread::sleep(10);
return 50;
}
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QHBoxLayout *mainLayout = new QHBoxLayout;
setLayout(mainLayout);
QProgressBar *bar = new QProgressBar;
bar->setMaximum(100);
bar->setMinimum(0);
mainLayout->addWidget(bar);
QPushButton *button = new QPushButton("开始");
mainLayout->addWidget(button);
QFutureWatcher<int> *watcher = new QFutureWatcher<int>(this);
connect(watcher,&QFutureWatcher<int>::finished,[=]{
bar->setValue(watcher->result());
});
connect(button,&QPushButton::clicked,[=]{
QFuture<int> future = QtConcurrent::run(getProcess);
watcher->setFuture(future);
});
}
实例3:
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
setWindowTitle(tr("Image loading and scaling example"));
resize(800,600);
//创建一个监视结果的对象
imageScaling = new QFutureWatcher<QImage>(this);
connect(imageScaling,&QFutureWatcher<QImage>::resultReadyAt,this,&Widget::showImage);
connect(imageScaling,&QFutureWatcher<QImage>::finished,this,&Widget::finished);
//布局
openButton = new QPushButton(tr("Open Images"));
connect(openButton,&QPushButton::clicked,this,&Widget::open);
cancelButton = new QPushButton(tr("Cancel"));
cancelButton->setEnabled(false);
connect(cancelButton,&QPushButton::clicked,
imageScaling,&QFutureWatcher<QImage>::cancel);
pauseButton = new QPushButton(tr("Pause/Resume"));
pauseButton->setEnabled(false);
connect(pauseButton,&QPushButton::clicked,
imageScaling,&QFutureWatcher<QImage>::togglePaused);
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(openButton);
buttonLayout->addWidget(cancelButton);
buttonLayout->addWidget(pauseButton);
buttonLayout->addStretch();
imagesLayout =new QGridLayout;
mainLayout = new QVBoxLayout;
mainLayout->addLayout(buttonLayout);
mainLayout->addLayout(imagesLayout);
mainLayout->addStretch();
setLayout(mainLayout);
}
Widget::~Widget()
{
imageScaling->cancel();
imageScaling->waitForFinished();
}
void Widget::open()
{
if(imageScaling->isRunning()){
imageScaling->cancel();
imageScaling->waitForFinished();
}
//打开文件选择框
QStringList files = QFileDialog::getOpenFileNames(this,"选择图片","F:/","Image(*.jpg *.png)");
if(files.isEmpty())
return;
const int imageSize = 100;
//清空之前的缩略图
qDeleteAll(labels);//删除指针指向的对象
labels.clear();//清空指针
//根据文件数量开方计算行列
int dim = qSqrt(qreal(files.count())) + 1;
for(int i = 0; i < dim; ++i){
for(int j = 0; j < dim; ++j){
QLabel *imageLabel = new QLabel;
imageLabel->setFixedSize(imageSize,imageSize);
imagesLayout->addWidget(imageLabel,i,j);//网格布局的位置
labels.append(imageLabel);
}
}
//c++中的lambda函数
std::function<QImage(const QString&)> scale = [imageSize](const QString &imageFileName){
QImage image(imageFileName);
return image.scaled(QSize(imageSize,imageSize),Qt::IgnoreAspectRatio,Qt::SmoothTransformation);
};
//按顺序为每个项调用一次函数,并返回每个映射项的结果对象
QFuture<QImage> future = QtConcurrent::mapped(files,scale);
imageScaling->setFuture(future);
openButton->setEnabled(false);
cancelButton->setEnabled(true);
pauseButton->setEnabled(true);
}
void Widget::showImage(int num)
{
labels.value(num)->setPixmap(QPixmap::fromImage(imageScaling->resultAt(num)));
}
void Widget::finished()
{
openButton->setEnabled(true);
cancelButton->setEnabled(false);
pauseButton->setEnabled(false);
}
实例4:
实例5:
class ReadWorker : public QObject
{
Q_OBJECT
public:
explicit ReadWorker(QObject *parent = nullptr);
~ReadWorker();
signals:
void readLine(const QString lineText);
public slots:
void readFile(const QString path);
};
ReadWorker::ReadWorker(QObject *parent) : QObject(parent)
{
}
ReadWorker::~ReadWorker()
{
qDebug() << __FUNCTION__;
}
void ReadWorker::readFile(const QString path)
{
QFile file(path);
if(file.exists()){
file.open(QIODevice::ReadOnly);
while(!file.atEnd()){
readLine(QString(file.readLine()));
QThread::msleep(500);
}
}else{
emit readLine("文件不存在");
}
}
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
signals:
void readFile(const QString file);
public slots:
void readLine(const QString lineText);
private:
QThread *m_thread;
QTextBrowser *m_browser;
};
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QPushButton *openButton = new QPushButton("打开文件");
connect(openButton,&QPushButton::clicked,[=]{
QString file = QFileDialog::getOpenFileName(this,"选择文件","F:/","Text(*.txt)");
if(file.isEmpty())
return;
m_browser->clear();
readFile(file);
});
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(openButton);
buttonLayout->addStretch();
m_browser = new QTextBrowser;
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(buttonLayout);
mainLayout->addWidget(m_browser);
setLayout(mainLayout);
m_thread = new QThread;
ReadWorker *worker = new ReadWorker;
connect(this,&Widget::readFile,worker,&ReadWorker::readFile);
connect(worker,&ReadWorker::readLine,this,&Widget::readLine);
connect(m_thread,&QThread::finished,worker,&ReadWorker::deleteLater);
worker->moveToThread(m_thread);
m_thread->start();
}
Widget::~Widget()
{
qDebug() << __FUNCTION__;
if(m_thread->isRunning()){
m_thread->quit();
m_thread->wait();
}
delete m_thread;
}
void Widget::readLine(const QString lineText)
{
m_browser->append(lineText);
}
实例6:实现一个每隔3秒截一次屏的程序
class PrintScreenThread : public QThread
{
Q_OBJECT
public:
PrintScreenThread(QObject *parent = 0);
protected:
void run();
signals:
void printScreen(const QPixmap pixmap);
};
PrintScreenThread::PrintScreenThread(QObject *parent): QThread(parent)
{
}
void PrintScreenThread::run()
{
for(int i = 0; i < 10; ++i){
QScreen *screen = QApplication::primaryScreen();
emit printScreen(screen->grabWindow(0));
sleep(3);
}
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QLabel *label = new QLabel("这里显示截屏图片");
setCentralWidget(label);
PrintScreenThread *thread = new PrintScreenThread(this);
connect(thread,&PrintScreenThread::printScreen,[=](const QPixmap pixmap){
label->setPixmap(pixmap);
});
thread->start();
}
3、线程间的同步(重点)
同步的方式介绍:
<1>QMutext用于强制互斥。线程锁定互斥锁以获得对共享资源的访问。如果第二个线程试图在互斥锁已经锁定的情况下锁定它,那么第二个线程将处于休眠状态,直到第一个线程完成任务并解锁互斥锁。
#include <QThread>
#include <QLabel>
class Widget;
class Thread: public QThread
{
Q_OBJECT
public:
explicit Thread(QObject * parent = 0);
protected:
void run();
signals:
void showLabel(QLabel *label);
private:
Widget *m_wgt;
};
#include "thread.h"
#include "widget.h"
Thread::Thread(QObject * parent)
:QThread(parent)
{
m_wgt = qobject_cast<Widget*>(parent);
}
void Thread::run()
{
QLabel *label = 0;
while(label = m_wgt->getLabel()){
sleep(1);
emit showLabel(label);
}
}
#include <QWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QLabel>
#include <QDebug>
#include <QMutex>
#include <QMutexLocker>
#include "thread.h"
class Thread;
class Widget: public QWidget
{
Q_OBJECT
public:
Widget(QWidget * parent = 0);
~Widget();
QLabel *getLabel();
public slots:
void addWidget1(QLabel *label);
void addWidget2(QLabel *label);
private:
QMutex m_mtx;
QGridLayout *grid;
QList<QLabel*> labels;
Thread *thread1;
Thread *thread2;
QVBoxLayout *vl1;
QVBoxLayout *vl2;
};
#include "widget.h"
#include "thread.h"
#include <QGroupBox>
Widget::Widget(QWidget * parent):QWidget(parent)
{
QGroupBox *gridBox = new QGroupBox("Labels");
grid = new QGridLayout;
gridBox->setLayout(grid);
for(int i = 0; i < 1; ++i){
for(int j = 0; j < 10; ++j){
QLabel *label = new QLabel(tr("Label%1").arg(j));
label->setStyleSheet(QString("background-color:lightgray"));
grid->addWidget(label,i,j);
labels.append(label);
}
}
QHBoxLayout *layout = new QHBoxLayout;
QGroupBox *box1 = new QGroupBox("Thread1");
vl1 = new QVBoxLayout;
box1->setLayout(vl1);
layout->addWidget(box1);
QGroupBox *box2 = new QGroupBox("Thread2");
vl2 = new QVBoxLayout;
box2->setLayout(vl2);
layout->addWidget(box2);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(gridBox);
mainLayout->addLayout(layout);
setLayout(mainLayout);
thread1 = new Thread(this);
thread2 = new Thread(this);
connect(thread1,&Thread::showLabel,this,&Widget::addWidget1);
connect(thread2,&Thread::showLabel,this,&Widget::addWidget2);
thread1->start();
thread2->start();
}
Widget::~Widget()
{
if(thread1->isRunning()){
thread1->quit();
thread1->wait();
}
if(thread2->isRunning()){
thread2->quit();
thread2->wait();
}
delete thread1;
delete thread2;
}
QLabel* Widget::getLabel()
{
QMutexLocker locker(&m_mtx);
if(labels.count() > 0){
return labels.takeFirst();
}else{
return 0;
}
}
void Widget::addWidget1(QLabel *label)
{
QLabel *newLabel = new QLabel(label->text());
grid->removeWidget(label);
label->hide();
delete label;
vl1->addWidget(newLabel);
}
void Widget::addWidget2(QLabel *label)
{
QLabel *newLabel = new QLabel(label->text());
grid->removeWidget(label);
label->hide();
delete label;
vl2->addWidget(newLabel);
}
#include <QApplication>
#include "widget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
<2>QReadWriteLock与QMutex类似,只是它区分了“读”和“写”访问。QReadWriteLock允许同时读操作,但是不能同时写(这是肯定的)。当一段数据没有被写入时,多线程同时读取它是安全的。QMutex强制多个读取器轮流读取共享数据,但QReadWriteLock允许同时读取,从而提高了并行性。这里是简单的介绍:https://baijiahao.baidu.com/s?id=1616183711867094589&wfr=spider&for=pc
#include <QThread>
#include <QLabel>
class Widget;
class ReadThread : public QThread
{
Q_OBJECT
public:
explicit ReadThread(QObject * parent = 0);
protected:
void run();
signals:
void showLabel(QLabel *label);
private:
Widget *m_wgt;
};
#include "readthread.h"
#include "widget.h"
ReadThread::ReadThread(QObject * parent)
:QThread(parent)
{
m_wgt = qobject_cast<Widget*>(parent);
}
void ReadThread::run()
{
QLabel *label = 0;
while(label = m_wgt->getLabel()){
sleep(2);
emit showLabel(label);
}
}
#include <QThread>
class Widget;
class WriteThread : public QThread
{
Q_OBJECT
public:
WriteThread(QString name, QObject * parent = 0);
protected:
void run();
signals:
void showLabel(const QString text);
private:
Widget *m_wgt;
QString m_name;
};
#include "writethread.h"
#include "widget.h"
WriteThread::WriteThread(QString name, QObject *parent):QThread(parent),m_name(name)
{
m_wgt = qobject_cast<Widget*>(parent);
}
void WriteThread::run()
{
int cnt = 0;
while(cnt < 10){
sleep(1);
QString text = QString("%1-%2").arg(m_name).arg(cnt);
emit showLabel(text);
cnt++;
}
}
#include <QWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QLabel>
#include <QDebug>
#include <QReadWriteLock>
class ReadThread;
class WriteThread;
class Widget: public QWidget
{
Q_OBJECT
public:
Widget(QWidget * parent = 0);
~Widget();
QLabel *getLabel();
void setLabel(QLabel *label);
public slots:
void readLabel1(QLabel *label);
void readLabel2(QLabel *label);
void readLabel3(QLabel *label);
void writeLabel1(const QString text);
void writeLabel2(const QString text);
private:
QHBoxLayout *buffer;
QList<QLabel*> labels;
QVBoxLayout *vl1;
QVBoxLayout *vl2;
QVBoxLayout *vl3;
QVBoxLayout *vl4;
QVBoxLayout *vl5;
QReadWriteLock m_readWriteLock;
ReadThread *readThread1;
ReadThread *readThread2;
ReadThread *readThread3;
WriteThread *writeThread1;
WriteThread *writeThread2;
};
#include "widget.h"
#include <QGroupBox>
#include "readthread.h"
#include "writethread.h"
Widget::Widget(QWidget * parent):QWidget(parent)
{
QGroupBox *bufferBox = new QGroupBox("Labels");
buffer = new QHBoxLayout;
//为了让读线程已启动不退出先放进几个让其先读取
for(int i = 0; i < 5; ++i){
QLabel *label = new QLabel(QString("test%1").arg(i));
buffer->addWidget(label);
labels.append(label);
}
bufferBox->setLayout(buffer);
QHBoxLayout *layout = new QHBoxLayout;
QGroupBox *readbox1 = new QGroupBox("ReadThread1");
vl1 = new QVBoxLayout;
readbox1->setLayout(vl1);
layout->addWidget(readbox1);
QGroupBox *readbox2 = new QGroupBox("ReadThread2");
vl2 = new QVBoxLayout;
readbox2->setLayout(vl2);
layout->addWidget(readbox2);
QGroupBox *readbox3 = new QGroupBox("ReadThread3");
vl3 = new QVBoxLayout;
readbox3->setLayout(vl3);
layout->addWidget(readbox3);
QGroupBox *writebox1 = new QGroupBox("WriteThread1");
vl4 = new QVBoxLayout;
writebox1->setLayout(vl4);
layout->addWidget(writebox1);
QGroupBox *writebox2 = new QGroupBox("WriteThread2");
vl5 = new QVBoxLayout;
writebox2->setLayout(vl5);
layout->addWidget(writebox2);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(bufferBox);
mainLayout->addLayout(layout);
setLayout(mainLayout);
readThread1 = new ReadThread(this);
readThread2 = new ReadThread(this);
readThread3 = new ReadThread(this);
writeThread1 = new WriteThread("WriteThread1",this);
writeThread2 = new WriteThread("WriteThread2",this);
connect(readThread1,&ReadThread::showLabel,this,&Widget::readLabel1);
connect(readThread2,&ReadThread::showLabel,this,&Widget::readLabel2);
connect(readThread3,&ReadThread::showLabel,this,&Widget::readLabel3);
connect(writeThread1,&WriteThread::showLabel,this,&Widget::writeLabel1);
connect(writeThread2,&WriteThread::showLabel,this,&Widget::writeLabel2);
readThread1->start();
readThread2->start();
readThread3->start();
writeThread1->start();
writeThread2->start();
}
Widget::~Widget()
{
if(readThread1->isRunning()){
readThread1->quit();
readThread1->wait();
}
if(readThread2->isRunning()){
readThread2->quit();
readThread2->wait();
}
if(readThread3->isRunning()){
readThread3->quit();
readThread3->wait();
}
if(writeThread1->isRunning()){
writeThread1->quit();
writeThread1->wait();
}
if(writeThread2->isRunning()){
writeThread2->quit();
writeThread2->wait();
}
delete readThread1;
delete readThread2;
delete readThread3;
delete writeThread1;
delete writeThread2;
}
QLabel* Widget::getLabel()
{
m_readWriteLock.lockForRead();
QLabel * label = 0;
if(labels.count() > 0){
label = labels.takeFirst();
}
m_readWriteLock.unlock();
return label;
}
void Widget::setLabel(QLabel *label)
{
m_readWriteLock.lockForWrite();
labels.append(label);
m_readWriteLock.unlock();
}
void Widget::readLabel1(QLabel *label)
{
QLabel *newLabel = new QLabel(label->text());
buffer->removeWidget(label);
label->hide();
delete label;
vl1->addWidget(newLabel);
}
void Widget::readLabel2(QLabel *label)
{
QLabel *newLabel = new QLabel(label->text());
buffer->removeWidget(label);
label->hide();
delete label;
vl2->addWidget(newLabel);
}
void Widget::readLabel3(QLabel *label)
{
QLabel *newLabel = new QLabel(label->text());
buffer->removeWidget(label);
label->hide();
delete label;
vl3->addWidget(newLabel);
}
void Widget::writeLabel1(const QString text)
{
QLabel *newLabel1 = new QLabel(text);
QLabel *newLabel2 = new QLabel(text);
buffer->addWidget(newLabel1);
vl4->addWidget(newLabel2);
setLabel(newLabel1);
}
void Widget::writeLabel2(const QString text)
{
QLabel *newLabel1 = new QLabel(text);
QLabel *newLabel2 = new QLabel(text);
buffer->addWidget(newLabel1);
vl5->addWidget(newLabel2);
setLabel(newLabel1);
}
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
<3>QSemaphore是QMutex的一个泛化,用于保护一定数量的相同资源。信号量的一个典型应用是控制对生产者线程和消费者线程共享的循环缓冲区的访问。它和QMutex、QReadWriteLock最大的不同就是,它可以让线程自动阻塞和唤醒。这样可以让线程在无事可做的时候不会退出。
#include <QCoreApplication>
#include <stdio.h>
#include <stdlib.h>
#include <QtCore>
const int DataSize = 100000;
const int BufferSize = 8192;
char buffer[BufferSize];
//创建一个新的信号量,并初始化它保护的资源数量
QSemaphore freeBytes(BufferSize);
QSemaphore usedBytes(0);
class Producer : public QThread
{
public:
void run() override
{
for(int i = 0; i < DataSize; ++i){
sleep(1);
freeBytes.acquire(1);//锁住1个空闲的资源,然后对空闲资源进行操作 比如赋值
buffer[i%BufferSize] = "ACGT"[QRandomGenerator::global()->bounded(4)];//随机从"ACGT"中产生一个字符
usedBytes.release(1);//通知增加1个可读资源
}
}
};
class Consumer : public QThread
{
Q_OBJECT
public:
void run() override
{
for(int i = 0; i < DataSize; ++i){
sleep(1);
usedBytes.acquire(1);//锁住有1个可读的资源 然后对可读资源进行操作,如果没有便会阻塞
fprintf(stderr,"%c",buffer[i%BufferSize]);
freeBytes.release(1);//通知释放1个空闲资源
}
fprintf(stderr,"\n");
}
signals:
void stringConsumed(const QString &text);
protected:
bool finish;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Producer producer;
Consumer consumer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
return 0;
return a.exec();
}
#include "main.moc"
<4>QWaitCondition不是通过强制互斥来同步线程,而是通过提供条件变量来同步线程。QWaitCondition使线程等待,直到满足特定的条件。。为了让等待的线程继续,可以调用wakeOne()来唤醒一个随机选择的线程,或者调用wakeAll()来同时唤醒所有线程。
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <QVector>
#include <QTime>
#include <QMutex>
#include <QWaitCondition>
const int bufferSize = 100;
QVector<char> buffer(bufferSize,'a');
QMutex mutex;
QWaitCondition produce;
QWaitCondition consume;
class ProducerThread: public QThread
{
void run()override
{
while (1) {
mutex.lock();
if(buffer.size() == bufferSize){
qDebug() << "生产者在等待...";
produce.wait(&mutex);
}
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
buffer.append((char)(97 + qrand() % 26));
consume.wakeAll();
mutex.unlock();
}
}
};
class ConsumerThread: public QThread
{
void run() override
{
while (1) {
mutex.lock();
if(buffer.isEmpty()){
qDebug() << "消费者在等待...";
consume.wait(&mutex);
}
qDebug() << buffer.takeFirst();
produce.wakeAll();
mutex.unlock();
}
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
ProducerThread thread1;
ConsumerThread thread2;
thread1.start();
thread2.start();
thread1.wait();
thread2.wait();
return 0;
}
<5>还可以利用Qt的事件系统,Qt的事件维护了一个队列,当不同的线程绑定了信号和槽,当发出信号时,对应的槽函数便会响应从而完成操作,由于分发队列的存在,这肯定是线程安全的。
4、线程可重入和线程安全的知识介绍(吹牛用的)
<1>多个线程可以同时调用的函数叫做 线程安全的函数,如果多个线程涉及到访问同一个资源则必须加锁,如果不加锁,则次函数不是线程安全的函数
<2>可重入函数包括线程安全函数。但是可重入函数不一定是线程安全的。
5、线程和对象模型之间的关系介绍(吹牛用的)
6、线程在Qt不同模块中是如何应用的(次重点)
<1>线程和SQL模块:连接只能在创建它的线程中使用。不支持在线程之间移动连接或从不同的线程创建查询。此外,QSqlDrivers使用的第三方库可以进一步限制在多线程程序中使用SQL模块。
<2>线程与绘图:可以在线程中使用QPainter来绘制QImage、QPrinter和QPicture绘制设备。不支持绘制到QPixmaps和QWidgets上。任何数量的线程都可以在任何给定的时间进行绘制,但是每次只有一个线程可以在给定的绘制设备上进行绘制。换句话说,如果两个线程分别绘制到不同的QImage上,那么两个线程可以同时绘制,但是两个线程不能同时绘制到相同的QImage上。
<3>线程和富文本处理:QTextDocument、QTextCursor和所有相关类都是可重入的。注意,在GUI线程中创建的QTextDocument实例可能包含QPixmap图像资源。使用QTextDocument::clone()创建文档的副本,并将副本传递给另一个线程进行进一步处理(如打印)。
<4>线程和SVG模块:QtSvg模块中的QSvgGenerator和QSvgRenderer类是可重入的。
<5>线程和隐式共享类:Qt对它的许多值类使用一种称为隐式共享的优化,尤其是QImage和QString。从qt4开始,隐式共享类可以安全地跨线程复制,就像其他任何值类一样。它们是完全可重入的。