Qt创建线程的几种方法

1. 继承QThread

继承QThread,这应该是最常用的方法了。我们可以通过重写虚函数void QThread:runO实现我们自己想做的操作,实现新建线程的目的。前面已经介绍了Qthread,这里就不重复了。
这种方法,
1.我们每一次要新建一个线程都需要继承Qthread,实现一个新的类,有点不太方便。但是相对于Qrunnable,这种方法的好处就是我们可以直接调用对象的start(0函数启动线程,而Qrunnables必须借助QthreadPool。
2. 如果在自定义的线程类 MyThread 中定义相关的槽函数,那么这些槽函数不会由子类化的QThread 自身事件循环所执行,而是由该子线程的拥有者(一般都是主线程)来执行

MyThread.h

//
// MyThread.h
//

#ifndef DEL2_MYTHREAD_H
#define DEL2_MYTHREAD_H

#include <QThread>
#include <QDebug>

class MyThread : public QThread
{
Q_OBJECT

public:
    explicit MyThread(QObject* parent = nullptr);

signals:
    void myThreadSignal(const int);  // 自定义发送的信号

public slots:
    static void myThreadSlot(int);  // 自定义槽函数

protected:
    void run() override ;
};

#endif //DEL2_MYTHREAD_H

MyThread.cpp

//
// MyThread.cpp
//

#include "MyThread.h"

MyThread::MyThread(QObject* parent)
{

}

void MyThread::run()
{
    qDebug() << "myThread run() start to execute" ;
    qDebug() << "\tCurrent thread ID:" << QThread::currentThreadId() << '\n' ;

    // 循环一百万次
    int count = 0 ;
    for (int i = 0; i != 1000000; ++i)
    {
        ++count ;
    }

    emit myThreadSignal(count);  // 发送结束信号
    exec();
}

void MyThread::myThreadSlot(const int val)
{
    qDebug() << "myThreadSlot() start to execute" ;
    qDebug() << "\tCurrent thread ID:" << QThread::currentThreadId() << '\n' ;

    // 循环一百万次
    int count = 888 ;
    for (int i = 0; i != 1000000; ++i)
    {
        ++count ;
    }
}

main() 函数如下:

#include <QApplication>

#include "Controller.h"

int main(int argc, char *argv[])
{
    qDebug() << "I am main Thread, my ID: " << QThread::currentThreadId() << "\n" ;

    QApplication a(argc, argv);

    Controller c ;

    return a.exec();
}

效果如下:
在这里插入图片描述
在这一种方法中,我们自定义了一个继承自 QThread 的类,实例化该类的对象,重载 run() 函数为需要做的工作在需要的地方调用 start 函数来执行 run 函数中的任务

2. 继承QRunnable

Qrunnable是所有可执行对象的基类。我们可以继承Qrunnable,并重写虚函数void QRunnable:run0。
我们可以用QThreadPooli让我们的一个QRunnable对象在另外的线程中运行,如果autoDelete()返回true(默认),那么QThreadPool将会在runO运行结束后自动删除Qrunnable对象。
可以调用void QRunnable:setAutoDelete(bool autoDelete)更改auto-deletion标记。需要注意的是,必须在调用QThreadPool::start().之前设置,在调用QThreadPool::start0之后设置的结果是未定义的。
例如

class Runnable:publicQRunnable
{
       //Q_OBJECT   注意了,Qrunnable不是QObject的子类。
public:
       Runnable();
       ~Runnable();

       voidrun();

};
 
 
Runnable::Runnable():QRunnable()
{
 
}
 
 
Runnable::~Runnable()
{
    cout<<"~Runnable()"<<endl;
}
 
 
 
void Runnable::run()
{
     cout<<"Runnable::run()thread :"<<QThread::currentThreadId()<<endl;
     cout<<"dosomething ...."<<endl;
}
 
int main(int argc, char *argv[])
{
     QCoreApplication a(argc, argv);
     cout<<"mainthread :"<<QThread::currentThreadId()<<endl;
     Runnable runObj;
     QThreadPool::globalInstance()->start(&runObj);
     returna.exec();
}

在这里插入图片描述
由结果可看出,ru0确实是在不同于主线程的另外线程中运行的,而且在运行结束后就调用了析构函数,因为默认是可以自动被销毁的。

我们可以对同一个对象多次调用QThreadPool:start(),如果是可以自动被销毁的,Qrunnable对象会在最后一个线程离开了run函数之后才被销毁的。
例如

int main(int argc, char *argv[])
{
       QCoreApplication a(argc, argv); 
       cout<<"mainthread :"<<QThread::currentThreadId()<<endl; 
       Runnable runObj; 

       QThreadPool::globalInstance()->start(&runObj);
       QThreadPool::globalInstance()->start(&runObj);
       QThreadPool::globalInstance()->start(&runObj);      
 
       returna.exec();
}

我三次调用QThreadPool::globallnstance0-》start(&runObj):,但是在三次都执行完之后才运行析构函数。
在这里插入图片描述
这种新建线程的方法的最大的缺点就是:不能使用Qt的信号一槽机制,因为Qrunnable?不是继承自QObject。,所曰以我们要想知道线程是否运行结束或获取运行结果可能会比较麻烦。还有就是我们不能直接调用run()启动线程,必须借助于QthreadPool。.

但是这种方法的好处就是,可以让QThreadPool来管理线程QThreadPool会自动的清理我们新建的Qrunnable对象。

3.使用moveToThread

首先我们必须实现继承QObiect的一个类,实现我们想要的功能

 class Worker:publicQObject
{
       Q_OBJECT
public:
       Worker(); 
       ~Worker();
 
 protected slots:
        void fun1();
        void fun2();
 
private:
 
};
 
Worker::Worker():QObject()
{        
}
 
 
 
Worker::~Worker()
{     
}
 
 
void Worker::fun1()
{
       cout<<"Worker::fun1()  thread : "<<QThread::currentThreadId()<<endl;
}

接着创建一个对象,并调用:moveToThread(QThread*targetThread),让对象在新的线程中运行.
例如:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    cout<<"mainthread :"<<QThread::currentThreadId()<<endl;
   
    QThread thread;
    Worker work;
    thread.start();              //注意记得启动线程
    work.moveToThread(&thread);
 
//由于不能直接调用worker
 
//的函数,所以一般用信号触发调用
 
    QTimer::singleShot(0,&work,SLOT(fun1()));  
    QTimer::singleShot(0,&work,SLOT(fun1()));  
 
    returna.exec();
}

这样就能让fun10和fun20都运行在thread:线程中了。
在这里插入图片描述
需要注意的是:在work的函数结束运行前,thread不能被析构。Thread的生命期不能小于work。否则的话程序就好
崩掉了。
像下面的代码肯定是不行的。

void Dialog::startWork()
{
      QThread thread;
      Worker*work = new Worker;
       thread.start();
       work->moveToThread(&thread);
       QTimer::singleShot(0,work,SLOT(fun1()));
       QTimer::singleShot(0,work,SLOT(fun2()));
 
}

所以thread 必须是new出来的,但是这样比较麻烦,因为我们需要同时管理thread 和 work,因为都是new出来的,我们需要负责清理。为了避免这个麻烦,想到的一个方法是在wrok类中添加一个QThread成员

例如:

class Worker:publicQObject
{
    Q_OBJECT
public:
    Worker();
    ~Worker();
 
protectedslots:
    voidfun1();
    voidfun2();
 
private:
	QThread m_thread;
 
};
 
Worker::Worker():QObject()
{
       m_thread.start();
       this->moveToThread(&m_thread);
}

这样在用的时候只要new work就可以了。

4.使用 QtConcurrent::run

QtConcurrent::run提供了很多方法可以实现并发编程。
例如:

void Worker::start()
{
       QtConcurrent::run(this,&Worker::fun1);
       QtConcurrent::run(this,&Worker::fun2);
}

在这里插入图片描述

QtConcurrent::run是一个模板函数,有很多种形式,我们也可以用全局的函数和传递参数的方式使用

void printMes(char*mes)
{
       cout<<"pprintMes(char*mes)  thread : "<<QThread::currentThreadId()<<endl;
       cout<<mes<<endl;
}
 
 
int main(int argc, char *argv[])
{
       QCoreApplication a(argc, argv);
       cout<<"mainthread :"<<QThread::currentThreadId()<<endl;
       char *mes= "hello world";
       QtConcurrent::run(printMes,mes);
       returna.exec();
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路过的小熊~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值