QProcess
使用QProcess.start(程序名称,命令参数)
启动进程的步骤:
QThread
要创建一个线程,必须子类化QThread
,并重新实现run()
函数 | 功能 |
---|---|
isFinished() | 查看线程状态 |
isRunning() | 查看线程状态 |
exec() | 启动事件循环 |
exit() 或 quit() | 停止事件循环 |
terminate() | 强行终止一个线程,不推荐 |
wait() | 跟随terminate()之后来同步终止 |
currentThreadId() | 返回当前线程的ID |
currentThread() | 返回当前线程的指针 |
首先,非常感谢两篇博文:
作为初学的话,建议仔细看,多看几遍这两篇文章,写得非常好~虽然刚开始看可能由于作者有很多具体的代码,以及很多情况,所以篇幅略长看不进去,但是多看几遍加自己一些思考,就可以初步学会怎么使用两种多线程建立的方法。我写这篇博文主要是再具体总结强调一些我实践过程中感觉到的重点部分,可能会和前面两篇博文有一些重合地方。
此方法最重要的是记住三点:
run
函数是在新建的线程中,而其他类函数都在原线程myThreadClass.start()
exec()
让事件循环,那么run
函数运行完即结束为什么要知道这三点,因为它们关乎线程有没有被成功建立和成功释放,否则就会出很大的事,比如线程没被释放的时候会不断创建线程导致代码被反复执行多次。
我抓住这几个重点写最简略的代码,防止其他内容干扰了理解。一些说明也写在了代码注释中。
MyThread.h
:创建线程类
#include
class MyThread:public QThread{
Q_OBJECT
public:
explicit MyThread(QObject *parent=0);
protected:
//run函数的重载是线程耗时事件执行的关键
void run();
};
MyThread.cpp
#include "MyThread.h"
#include
MyThread::MyThread(QObject *parent) :
QThread(parent)
{
}
void MyThread::run()
{
//在此写上耗时操作
}
dialog.h
:主窗口的类
#ifndef DIALOG_H
#define DIALOG_H
#include
#include "mythread.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private slots:
void clicked_thread_in(); //对应UI界面点击按钮进入线程
private:
Ui::Dialog *ui;
MyThread thread; //建立需要的线程,如果是全局线程就这么做;最后析构函数也会被释放
};
#endif // DIALOG_H
dialog.cpp
:要使用线程在对应地方start即可
include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
//连接按钮和槽
connect(ui->pushButton,&QPushButton::clicked,this,&Widget::clicked_thread_in);
}
Dialog::~Dialog()
{
delete ui;
}
// 启动线程按钮
void Dialog::clicked_thread_in()
{
//使用线程
thread.start();
}
上面是全局线程怎么做,但是更常用的是局部线程,只在用的时候开出来,那么需要做点改动:
dialog.h:
主窗口的类中用指针声明线程
private:
MyThread *thread;
dialog.cpp:
定义线程并且配合QObject::deleteLater
// 启动线程按钮
void Dialog::clicked_thread_in()
{
//定义线程
thread = new MyThread();
//*********让线程结束后自动销毁的关键
connect(thread,&QThread::finished,thread,&QObject::deleteLater)
//使用线程
thread->start();
}
qt官方文档,这是必须先通读一遍的代码!
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
同样先提下类似与上面关键的三个地方:
workThread.start()
,道理是和前面的方法一样,都是利用线程类QThread
启动,但是因为此时我们定义的类继承自QObject
所以不是用定义的类对象来启动了quit()
函数就起到关键作用了,因为官方说明其作用并不是让线程退出和结束,而是退出线程循环,如果不退循环线程是一直在的。官方介绍的是如何建立全局线程了,那我就在下面讲怎么创建使用更多的局部线程,这点是前面两篇文章可能漏写的,以及官方文档没写的。
为了方便理解,下述代码执行流程是:
MyThreadObj.h
:创建线程类
#include
class MyThreadObj : public QObject{
Q_OBJECT
public:
explicit MyThreadObj(QObject *parent=0);
protected:
//可以定义多个工作函数,为了代码简单就只写一个
void work1();
signals:
//表示可以暂停线程了
void work1_ready();
};
MyThreadObj.cpp
#include "MyThreadObj.h"
#include
MyThreadObj::MyThreadObj(QObject *parent) :
QObject (parent)
{
}
void MyThreadObj::work1()
{
//在此写上耗时操作
...
//此外一定要发射一个信号表示可以暂停线程了
emit work1_ready();
}
dialog.h
:主窗口的类
#ifndef DIALOG_H
#define DIALOG_H
#include
#include "MyThreadObj.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private slots:
void clicked_thread_in(); //对应UI界面点击按钮进入线程
void work1_ready_call(); //来自线程类发射的work1_ready信号
private:
Ui::Dialog *ui;
MyThread *thread; //关键:一定要先声明需要的线程,不可以局部声明,否则没法释放
};
#endif // DIALOG_H
dialog.cpp
:要使用线程在对应地方start即可
include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
//连接按钮和槽
connect(ui->pushButton,&QPushButton::clicked,this,&Widget::clicked_thread_in);
}
Dialog::~Dialog()
{
delete ui;
}
// 启动线程按钮
void Dialog::clicked_thread_in()
{
//建立使用线程类
MyThreadObj *obj1 = new MyThreadObj();
//定义新的线程,关键:线程绝对不可以有父对象
thread = new QThread(NULL);
//线程结束的时候释放类对象,一定要有
connect(thread,&QThread::finished,obj1,&QObject::deleteLater)
//连接线程完成发射信号
connect(obj1,&MyThreadObj::work1_ready,this,&Dialog::work1_ready_call);
//开启线程
thread->start();
}
//线程类的信号应答,并关闭线程
void Dialog::work1_ready_call(){
thread->quit(); //让线程退出循环,此处即可退出线程
thread->wait(); //等待线程释放
}
那么问题来了,前面说的那种出事是怎么样?
先看看正常情况
out: “MVIRDD::MVIRDD -> 主线程 thread id:19424”
out: 子线程dataTrans线程start
out: TimeuseWorker::dataTrans -> 子线程dataTrans, thread id:16676
out: 数据传输线程启动
out: 数据传输完成
再看看不正常情况
按理说每次按按钮都是上面这样,但是如果线程没被释放,就会重复执行,多按几次程序就奔溃了
out: “MVIRDD::MVIRDD -> 主线程 thread id:18440”
按一次按钮
out: 子线程dataTrans线程start
out: TimeuseWorker::dataTrans -> 子线程dataTrans, thread id:10124
out: 数据传输线程启动
out: 数据传输完成
按两次按钮
out: 子线程dataTrans线程start
out: TimeuseWorker::dataTrans -> 子线程dataTrans, thread id:10124
out: 数据传输线程启动
out: 数据传输线程启动
out: 数据传输完成
out: 数据传输完成
方法 | 含义 |
---|---|
QMutex | 互斥锁: 只允许一个线程访问变量 |
QReadWriteLock | 读写锁: 允许多个线程“读”,但只能一个线程“写” |
QSemaphore | 信号量: QMutex只能保护一个量,而它可以保护多个数量的相同资源 |
QWaitCondition | 条件变量: 允许一个线程在一些条件满足时唤醒其他线程 |
#include
#include
#include
#include
const int DataSize = 10; //写入和读取的次数
const int BufferSize = 5; //缓存大小
char buffer[BufferSize]; //缓存空间,超出范围从头开始写
QSemaphore freeBytes(BufferSize); //producer未写或consumer已读
QSemaphore usedBytes; //producer已写或consumer未读
// 生产者
class Producer : public QThread
{
public:
void run();
};
void Producer::run()
{
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
for (int i = 0; i < DataSize; ++i) {
freeBytes.acquire();
buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
qDebug() << QString("producer: %1").arg(buffer[i % BufferSize]);
usedBytes.release();
}
}
// 消费者
class Consumer : public QThread
{
public:
void run();
};
void Consumer::run()
{
for (int i = 0; i < DataSize; ++i) {
usedBytes.acquire();
qDebug() << QString("consumer: %1").arg(buffer[i % BufferSize]);
freeBytes.release();
}
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Producer producer;
Consumer consumer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
return app.exec();
}
某次运行的结果
次数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
producer | G | T | C | C | A | G | G | T | G | T | ||||||||||
consumer | G | T | C | C | A | G | G | T | G | T |