Qt·开启线程的五种方式

本文介绍了在Qt开发中使用线程的五种方法,包括继承QThread重写run函数、使用moveToThread、结合QRunnable和QThreadPool实现线程池、C++11的sth::thread以及QtConcurrent的Run函数。重点讨论了线程池的内存管理和QtConcurrent的并发特性。
摘要由CSDN通过智能技术生成

简介

在开发过程中,使用线程是经常会遇到的场景,本篇文章就来整理一下 Qt 中使用线程的五种方式,方便后期回顾。前面两种比较简单,一笔带过了,主要介绍后面三种。最后两种方法博主最喜欢,不需要继承类,可以直接把需要执行的函数放到线程中去运行

1. 继承 QThread 重写 run 函数

class Thread : public QThread
{
    Q_OBJECT
public:
    virtual void run() override;
}
void Thread::run()
{
    ...
}

  • 可调用 thread.start()启动线程,会自动调用 run 函数
  • 可调用 thread.isRunning()判断线程是否已启动
  • 可调用 thread.terminate()终止线程
  • 可调用 thread.wait()等待线程终止

2. 继承 QObject 调用 moveToThread

class Test : public QObject
{
    Q_OBJECT
public:
    void test();
}
QThread th;
Test test;
test.moveToThread(&th);

需要注意的是:此方法与继承 QThread 相比较,继承 QThread 只有 run 函数中的操作是在线程中执行的,而此方法中所有的成员函数都是在线程中执行

3. 继承 QRunnable 重新 run 函数,结合 QThreadPool 实现线程池

#include <QObject>
#include <QRunnable>
#include <QThread>
#include <QThreadPool>
#include <QDebug>

class BPrint : public QRunnable
{
    void run()
    {
        for ( int count = 0; count < 5; ++count )
        {
            qDebug() << QThread::currentThread();
            QThread::msleep(1000);
        }
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
 
    QThreadPool threadpool;                       // 构建一个本地的线程池
    threadpool.setMaxThreadCount(3);        // 线程池中最大的线程数
    
    for ( int num = 0; num < 100; ++num )
    {
        BPrint *print;                        // 循环构建可在线程池中运行的任务
        threadpool.start(print);              // 线程池分配一个线程运行该任务
        QThread::msleep(1000);
    }
    
    return a.exec();
}

在上述例子当中,我们创建的 QRunnable 类型的指针 BPrint *print 是不需要我们手动去回收内存的,QThreadPool 在结束该任务的执行后会将对该内存进行清空

有的小伙伴会有疑问,既然有 QThread 线程类了,为啥还要用 QRunnable + QThreadPool 创建线程池的方法来使用线程机制呢,感觉用起来很麻烦啊。所以这里要说明一下此方法的使用场景,当线程任务量非常大的时候,如果频繁的创建和释放 QThread 会带来非常大的内存开销,而线程池则可以有效避免这个问题

还有一个问题需要注意一下,QThread 是集成自 QObject 的,我们通常会使用信号槽与外界进行通信。而 QRunnable 并不是继承自 QObject 类的,所以他无法使用信号槽机制进行通信。这里推荐两种方法,一个是使用 QMetaObject::invokeMethod()函数。另一个是使用多重继承的方法,自定义类需要同时继承自 QRunnable 和 QObject

4. 使用 C++ 11 中的 sth::thread

#include <thread>
void threadfun1()
{
    std::cout << "threadfun1 - 1\r\n" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "threadfun1 - 2" << std::endl;
}

void threadfun2(int iParam, std::string sParam)
{
    std::cout << "threadfun2 - 1" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "threadfun2 - 2" << std::endl;
}

int main()
{
    std::thread t1(threadfun1);
    std::thread t2(threadfun2, 10, "abc");
    t1.join();        // 等待线程 t1 执行完毕
    std::cout << "join" << std::endl;
    t2.detach();    // 将线程 t2 与主线程分离
    std::cout << "detach" << std::endl;
}

运行结果:
threadfun1 - 1
threadfun2 - 1

threadfun1 - 2
join
detach

根据输出结果可以得知,t1.join() 会等待t1线程退出后才继续往下执行,t2.detach() 并不会等待,detach字符输出后,主函数退出,threadfun2还未执行完成,但是在主线程退出后,t2的线程也被已经被强退出

5. Qt QtConcurrent 之 Run 函数

Concurrent 是并发的意思,QtConcurrent 是一个命名空间,提供了一些高级的 API,使得所写的程序可根据计算机的 CPU 核数,自动调整运行的线程数目。这意味着今后编写的应用程序将在未来部署在多核系统上时继续扩展

函数原型如下:
QFuture<T> QtConcurrent::run(Function function, ...)
QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...)

简单来说,QtConcurrent::run() 函数会在一个单独的线程中执行,并且该线程取自全局 QThreadPool,该函数的返回值通过 QFuture API 提供

需要注意的是:
1)该函数可能不会立即运行; 函数只有在线程可用时才会运行
2)通过 QtConcurrent::run() 返回的 QFuture 不支持取消、暂停,返回的 QFuture 只能用于查询函数的运行/完成状态和返回值
3) Qt Concurrent 已经从 QtCore 中移除并成为了一个独立的模块,所以想要使用 QtConcurrent 需要在 pro 文件中导入模块:
QT += concurrent

使用方式有以下几种:
1)将函数运行在某一个线程中,需要使用 extern

extern void func();
QFuture<void> future = QtConcurrent::run(func);

2)向该函数传递参数

extern void FuncWithArguments(int arg1, const QString &string);

int integer = ...;
QString string = ...;
// 需要传递的参数,则跟在函数名之后,依次加入
QFuture<void> future = QtConcurrent::run(FuncWithArguments, integer, string);    

3) 获取该函数的计算结果

extern QString Func(const QByteArray &input);

QByteArray byte_array = ...;
QFuture<QString> future = QtConcurrent::run(func, byte_array);
...
QString result = future.result();

4)常量成员函数

QByteArray bytearray = "hello world";
// 在一个单独的线程中,调用 QByteArray 的常量成员函数 split(),传递给 run() 函数的参数是 bytearray
QFuture< QList<QByteArray> > future = QtConcurrent::run(bytearray, &QByteArray::split, ',');
...
QList<QByteArray> result = future.result();

5)非常量成员函数

QImage image = ...;
// 在一个单独的线程中,调用 QImage 的非常量成员函数 invertPixels(),传递给 run() 函数的参数是 &image
QFuture<void> future = QtConcurrent::run(&image, &QImage::invertPixels, QImage::InvertRgba);
...
future.waitForFinished();

6)Lambda 表达式

#include <QFuture>
#include <QtConcurrent>
#include <QThreadPool>

QThreadPool pool;
QFuture<void> future = QtConcurrent::run(&pool, [&](QObject *receiver){
    cv::Mat mat = QYImageProcessing::convertQImageToMat(image);
    cv::Mat center = cv::imread("dynaPhase_center.png");
    
    dynaPhase_alive = QYImageProcessing::getDiffPoint(mat, center);
    
    // 根据三个点自适应模拟条纹
    cv::Mat ret = DynamicCarrier::DC_Adaptive_Simulation(dynaPhase_center, dynaPhase_alive, dynaPhase_align);
    ret = ret*255;
    ret.convertTo(ret, CV_8UC1);
    QImage adaptive = QYImageProcessing::convertMatToQImage(ret);
    
    QYAlignControl *align = static_cast<QYAlignControl *>(receiver);
    align->callQmlGetAlivePoint(adaptive, dynaPhase_alive.x, dynaPhase_alive.y);
}, this);


 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Qt中,可以使用Qt提供的多线程类来开启线程Qt中的多线程模块是基于QObject的信号与槽机制实现的,并提供了多种方式开启线程,包括: 1. 继承QThread类:可以通过继承QThread类并重写其run()函数来创建一个新的线程。 ```cpp #include <QThread> #include <QDebug> class MyThread : public QThread { Q_OBJECT public: void run() override { qDebug() << "这是一个新线程"; // 在这里执行线程的逻辑 // ... } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); MyThread thread; thread.start(); // 开启线程 return a.exec(); } ``` 在上述示例中,我们创建了一个名为MyThread的类,并继承了QThread。重写了run()函数,在其中实现了新线程的逻辑。然后创建一个MyThread对象,并调用start()函数来开启线程。 2. 使用QtConcurrent库:QtConcurrent库提供了一种方便的方式来在多个线程中执行函数或Lambda表达式。 ```cpp #include <QtConcurrent> void myFunction() { qDebug() << "这是一个新线程"; // 在这里执行线程的逻辑 // ... } int main(int argc, char *argv[]) { QApplication a(argc, argv); QtConcurrent::run(myFunction); // 在新线程中执行函数 return a.exec(); } ``` 在上述示例中,我们使用QtConcurrent::run()函数来执行myFunction函数,该函数会在一个新线程中运行。 3. 使用QThreadPool和QRunnable:通过将任务封装到QRunnable对象中,并将其提交给QThreadPool来实现多线程。 ```cpp #include <QThreadPool> #include <QDebug> class MyRunnable : public QRunnable { public: void run() override { qDebug() << "这是一个新线程"; // 在这里执行线程的逻辑 // ... } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); MyRunnable *runnable = new MyRunnable; QThreadPool::globalInstance()->start(runnable); // 提交任务给线程池 return a.exec(); } ``` 在上述示例中,我们创建了一个名为MyRunnable的类,并继承了QRunnable。重写了run()函数,在其中实现了新线程的逻辑。然后创建一个MyRunnable对象,并将其提交给QThreadPool的start()函数来开启线程。 无论采用哪种方式开启线程,都需要在Qt应用程序的事件循环中调用exec()函数,以保持应用程序的运行。也可以使用QThread的exec()函数来开启一个事件循环。 需要注意的是,在多线程编程中,需要遵守Qt线程安全规则,并使用适当的线程间通信机制(如信号与槽)来保证线程之间的协调与同步。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值