QT中的线程、线程池以及进程

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);
}

在这里插入图片描述

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值