qt多线程使用方式
  itL1q2DCWCoh 2023年11月02日 52 0


有5个方式:可以参考这个博客:Qt 中开启线程的五种方式_qt 线程_lucky-billy的博客

注:为了实现更加灵活的线程管理(因为这5种都有一些不方便之处:QThread需要子类化且不能传参,moveToThread不能传参且只能移入一个对象而不是一个函数,QThreadPool 需要子类化QRunnable且只能启动一个对象,QtConcurrent支持传参但是不能管理它,std::thread能够传入函数以及给定实参但是不是qt方式了),我自己实现了一个更好用的线程管理类(支持指定一个函数,同时指定传入的实参,而且能管理该线程例如随时终止等),看我这个博客: qt自己实现方便的线程管理类_我是标同学的博客

接下来,讲解这5种方式情况: 

1. 继承 QThread 重写 run 函数

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


优点:构造使用方式简单,所有操作都放在run()方法里面。而且可以随时停止,因为这是以线程对象形式存在,我们可以对其进行各种管理。
缺点:不够灵活,只能运行run()里面的程序。此外,传入的函数不能同时给参数,只能另外一个函数设置其成员变量,run函数里读取该成员变量。

2. 继承 QObject 调用 moveToThread

QThread th;
Test test;
test.moveToThread(&th);
优点:使用简单,可以使用信号槽通信,很灵活。
缺点:每次都要创建线程,带来不必要资源开支。而且是整个对象给弄进去,也挺重的。

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

我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

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

每个Qt应用程序都有一个全局 QThreadPool 对象,可以通过调用 globalInstance() 来访问它。==也可以单独创建一个 QThreadPool 对象使用。

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



一般情况下,我们不需要在 Qt 程序中创建线程池对象,**直接使用 Qt 为每个应用程序提供的线程池全局对象即可。得到线程池对象之后,调用 start() 方法**就可以将一个任务添加到线程池中,这个任务就可以被线程池内部的线程池处理掉了,使用线程池比自己创建线程的这种多种多线程方式更加简单和易于维护。
 

4. 使用 C++ 11 中的 std::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;
 }


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

5. Qt QtConcurrent 之 Run 函数

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

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

...

QString result = future.result();

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

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
itL1q2DCWCoh