1 线程、进程概念
-
1.程序与进程的关系
一个程序可能对应多个进程
一个进程可能包含多个程序 -
2.进程与线程的关系
进程是操作系统资源分配的基本单位
线程是操作系统调度执行的基本单位
进程中,包含一个或者多个线程,并且每个线程都可以共享进程的资源
线程中,可以创建和撤销其他的线程。线程不能脱离进程单独存在,只能依赖于进程。
2 QT线程
2.1 线程使用
在QT中,通过QThread类来对线程进行管理,通常情况线程创建有两种方法。
A QThread object manages one thread of control within the program. QThreads begin executing in run(). By default, run() starts the event loop by calling exec() and runs a Qt event loop inside the thread.
- You can use worker objects by moving them to the thread using QObject::moveToThread().
- Another way to make code run in a separate thread, is to subclass QThread and reimplement run().
1.run():用户自定义函数体执行的内容,可以使用exec()来实现事件循环
[virtual protected] void QThread::run()
2.信号:
表示线程已启动
[signal] void QThread::started()
表示线程已完成
[signal] void QThread::finished()
3.槽:
告诉线程事件循环退出
[slot] void QThread::quit()
启动函数,将会执行run()函数,并且发射信号started()
[slot] void QThread::start(QThread::Priority priority = InheritPriority)
表示线程已停止
[slot] void QThread::terminate()
4.延时函数
[static] void QThread::sleep(unsigned long secs)
[static] void QThread::usleep(unsigned long usecs)
[static] void QThread::msleep(unsigned long msecs)
5.成员方法:
设置线程的名字
setObjectName(const QString &)
设置线程的优先级
setPriority(QThread::Priority )
线程的基本使用:
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
class MyThread : public QThread
{
public:
explicit MyThread(QWidget *parent = nullptr){}
~MyThread(){}
virtual void run();
};
#endif // MYTHREAD_H
mythread.cpp
void MyThread::run()
{
while(1)
{
qDebug() << "MyThread::run()";
this->sleep(1);
}
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
th1 = new MyThread();//MyThread *th1;
th1->setObjectName("th1");//设置线程名称
th1->setPriority(QThread::HighPriority);//设置线程优先级
connect(th1,&MyThread::started,
this,&MainWindow::threadStarted);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_startBtn_clicked()
{
th1->start();//启动线程
}
void MainWindow::on_stopBtn_clicked()
{
th1->terminate();//终止线程
}
void MainWindow::threadStarted()
{
qDebug() << "MainWindow::threadStarted()";
}
经典实例:使用线程获取系统时间
getsystemtimethread.h
#ifndef GETSYSTEMTIMETHREAD_H
#define GETSYSTEMTIMETHREAD_H
#include <QThread>
class GetSystemTimeThread : public QThread
{
Q_OBJECT
public:
explicit GetSystemTimeThread(QWidget *parent = nullptr){}
~GetSystemTimeThread(){}
virtual void run()
{
while(1)
{
QString time = QTime::currentTime().toString("hh:mm:ss");
emit sendTime(time);//把当前获取的时间使用信号发送出去
QThread::usleep(1000);
}
}
signals:
sendTime(const QString time);
};
#endif // GETSYSTEMTIMETHREAD_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
timeManger = new GetSystemTimeThread();//GetSystemTimeThread *timeManger;
timeManger->start();
connect(timeManger,&GetSystemTimeThread::sendTime,
this,&MainWindow::showUpSystemTime);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::showUpSystemTime(QString time)
{
ui->label->setText(time);
}
2.2 线程同步
在QT中线程同步,使用QMutex—互斥锁
对于共享数据结构或者代码段,在多线程中,一次只允许有一个线程访问。
1.lock():Locks the mutex.
If another thread has locked the mutex then this call will block until that thread has unlocked it.
void QMutex::lock()
2.unlock():Unlocks the mutex.
void QMutex::unlock()
mainwindow.h
static int number;
static QMutex mutex;
class MyThread1 : public QThread
{
Q_OBJECT
public:
explicit MyThread1(QWidget *parent = nullptr){}
~MyThread1(){}
virtual void run()
{
while(1)
{
mutex.lock();
qDebug() << "MyThread1------" << number++;
QThread::sleep(1);
mutex.unlock();
}
}
};
class MyThread2 : public QThread
{
Q_OBJECT
public:
explicit MyThread2(QWidget *parent = nullptr){}
~MyThread2(){}
virtual void run()
{
while(1)
{
mutex.lock();
qDebug() << "MyThread2------" << number++;
QThread::sleep(2);
mutex.unlock();
}
}
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
MyThread1 *th1;
MyThread2 *th2;
};
mainwindow.cpp
th1 = new MyThread1();
th2 = new MyThread2();
th1->start();
th2->start();
3 线程池
3.1 线程池简单实用
QThreadPool,它是一个管理线程的管理者,把线程看作是一个任务——QRunnable,对于一个程序而言,管理者只有一个,采用的是单例模式实现,由它来启动一个任务和删除一个任务。
1.QRunnable是一个抽象类,纯虚函数run()
[pure virtual] void QRunnable::run()
virtual void run() = 0
2.QThread获取当前线程和线程号
[static] QThread *QThread::currentThread()
[static] Qt::HANDLE QThread::currentThreadId()
3.QThreadPool单例模式(管理者)
[static] QThreadPool *QThreadPool::globalInstance()
Returns the global QThreadPool instance.
.h
class MyTaskRunnable1 : public QRunnable
{
public:
explicit MyTaskRunnable1(QWidget *parent = nullptr){}
~MyTaskRunnable1(){}
virtual void run()
{
while(1)
{
qDebug() << "--------" << QThread::currentThread() << QThread::currentThreadId();
QThread::sleep(1);
}
}
};
.cpp
//创建任务一
mr1 = new MyTaskRunnable1();//MyTaskRunnable1 *mr1;
//创建任务二
mr2 = new MyTaskRunnable2();//MyTaskRunnable2 *mr2;
//将任务放入到线程池中
QThreadPool *manger = QThreadPool::globalInstance();
manger->start(mr1);
manger->start(mr2);
3.2 线程池跟新UI组件的方法
由于QRunnable是一个独立的类,不继承与QOBject,默认情况下是不能使用信号与槽。
方案一:采用多继承(推荐使用方案一)
.h核心代码
class MyTaskRunnable1 : public QObject, public QRunnable
{
Q_OBJECT
public:
explicit MyTaskRunnable1(QWidget *parent = nullptr){}
~MyTaskRunnable1(){}
virtual void run()
{
int i = 0;
while(1)
{
qDebug() << "--------" << QThread::currentThread() << QThread::currentThreadId();
sendData(QString::number(i++));
QThread::sleep(1);
}
}
signals:
sendData(QString data);
};
.cpp核心代码
connect(mr1,&MyTaskRunnable1::sendData,
this,&MainWindow::ShowUpData);
void MainWindow::ShowUpData(QString data)
{
ui->dataLabel->setText(data);
}
方案二:采用QMetaObject::invokeMethod
[static] bool QMetaObject::invokeMethod(QObject *obj,
const char *member,
Qt::ConnectionType type,
QGenericReturnArgument ret,
QGenericArgument val0 = QGenericArgument(nullptr),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument())
参数一:父类界面
参数二:信号与槽函数名
参数三:链接类型
参数四:返回值类型
va10——va19:参数列表,槽函数的参数列表
class MyTaskRunnable2 : public QRunnable
{
public:
explicit MyTaskRunnable2(QWidget *parent = nullptr)
{
this->w = parent;
}
~MyTaskRunnable2(){}
virtual void run()
{
int i = 0;
while(1)
{
QString str = QString::number(i++);
bool ret = QMetaObject::invokeMethod((MainWindow*)w,"showUpData1",Q_ARG(QString,str));
qDebug() << "--------" << QThread::currentThread() << QThread::currentThreadId();
QThread::sleep(2);
}
}
QWidget* w;
};
4 线程池与多线程的区别
1.线程的创建及销毁需要系统的交互,会产生很大的开销,若是需要频繁的创建线程,建议使用线程池。如果需要多线程来执行任务时,只需要将任务的指令传递给线程池即可,线程池会根据可用线程进行任务安排。
2.线程池防止线程之间的竞争资源访问。
5 进程
在QT中,使用QProcess类来完成进程间操作:启动额外应用程序和与其他的应用程序之间进行通信。
为了启动一个额外的程序,第一,可以直接使用程序的名字;第二,可以使用程序的额命令来做start的函数参数,或者通过setProgram()和setArguments(),他们两个程序必须在start之前进行,在start之后,程序进入start状态,再进入running状态,并且发送stared()信号,此外,QProcess继承于QIODevice类,所以也可以进行读写操作。
1.QProcess启动方式:
(1)一体式启动:
void QProcess::start(const QString &program,
const QStringList &arguments,
QIODevice::OpenMode mode = ReadWrite)
void QProcess::start(const QString &command,
QIODevice::OpenMode mode = ReadWrite)
void QProcess::start(QIODevice::OpenMode mode = ReadWrite)
(2)分离式启动:
bool QProcess::startDetached(qint64 *pid = nullptr)
[static] bool QProcess::startDetached(const QString &program,
const QStringList &arguments,
const QString &workingDirectory = QString(),
qint64 *pid = nullptr)
2.kill()和waitForFinished()
(1)[slot] void QProcess::kill()
Kills the current process, causing it to exit immediately.
On Windows, kill() uses TerminateProcess, and on Unix and macOS, the SIGKILL signal is sent to the process.
从字面意思上来看,会立即终止程序,但是实际上并不会,所以结合waitForFinished()来使用。
(2)bool QProcess::waitForFinished(int msecs = 30000)
Blocks until the process has finished and the finished() signal has been emitted,
or until msecs milliseconds have passed.
Returns true if the process finished;
otherwise returns false (if the operation timed out,
if an error occurred, or if this QProcess is already finished).
等待完成,默认等待时间为3s。
3.state()
QProcess::ProcessState QProcess::state() const
Returns the current state of the process.
状态表:
Constant Value Description
QProcess::NotRunning 0 The process is not running.
QProcess::Starting 1 The process is starting, but the program has not yet been invoked.
QProcess::Running 2 The process is running and is ready for reading and writing.
1.一体式启动自己写的应用程序(绝对路径)
void MainWindow::on_noteLabel_clicked()
{
if(mPr->state() == QProcess::Running)
{
mPr->kill();
mPr->waitForFinished();//默认30000ms
}
mPr->start("D:/YQ/10QT/mycode/0315/build-qt_fileDialog-Desktop_Qt_5_12_1_MinGW_64_bit-Debug/debug/qt_fileDialog.exe");
}
2.分离式启动系统自带计算器
void MainWindow::on_calaLabel_clicked()
{
if(mPr->state() == QProcess::Running)
{
mPr->kill();
mPr->waitForFinished();
}
mPr->startDetached("calc");
}
3.使用匿名对象启动QQ
void MainWindow::on_qqLabel_clicked()
{
(new QProcess)->start("E:/工具/PDF/Tencent/QQ/Bin/QQScLauncher.exe");
}
进程实例:模拟电脑命令提示符
cmd = new QProcess();//QProcess *cmd;
connect(cmd,&QProcess::readyRead,
this,&MainWindow::readData);
void MainWindow::on_runBtn_clicked()
{
QString cmdstr = ui->lineEdit->text();
cmd->start(cmdstr);
}
void MainWindow::readData()
{
QByteArray arr = cmd->readAll();
QTextCodec *codec = QTextCodec::codecForName("GB18030");
QString string = codec->toUnicode(arr);
ui->textBrowser->append(string);
}