• Qt高级编程技巧(二)-- 编写多线程和并发应用


    学习QT多线程编程之前,有必要先熟悉事件循环的概念。先看一个单线程界面程序的主函数代码:

    int main(int argc, char* argv[])
    {
      QApplication app(argc,  argv);
    
      // 构造主窗口对象并显示
      MainWindow w;
      w.show();
    
      // 进入事件循环
      return app.exec();
    }
    

    在程序初始化完成后,主线程进入main()函数开始执行应用代码。一般地,我们在主线程上构建界面对象,然后进入事件循环以处理控件绘制、用户输入、系统输出等消息。这就是我们通常说的事件驱动模型。

    主线程承担着用户交互的重任,当在主线程上运行费时的代码时,就会影响用户的正常操作。所以我们常把一些费时费力的计算工作移出主线程,开辟新的线程来运行之。

    QThread是QT中用于线程管理的类,调用一个QThread对象的start()方法时,会创建一个新的线程并执行它的run()方法。默认地,run()会调用exec()方法进入自己的消息循环中。如下图所示:

    上图中有主线程、工作线程都是执行事件循环,并且注意到主线程内部含有thr、w、objs这些QObject对象(这些对象都是在主线程上创建的)。主线程的事件循环负责检测这些对象是否有消息要处理,有的话则调用对象的slot方法。可以使用QObject::moveToThread方法将某个对象移到其他线程中,譬如:

    class Worker : public QObject {
        Q_OBJECT
        …
    }
    
    void someFunc()
    {
       QThread thr = new QThread;
       Worker worker = new Worker;
       worker->moveToThread(thr);
       thr->start();
      …
    }
    

    如果在主线程上调用someFunc(),则workerThread和worker在创建后都是关联在主线程上,当调用worker->moveToThread()后,worker对象关联到了新的线程中,如图所示:

    假定我们在MainWindow上声明了一个worksSignal()消息,在Worker对象上声明和定义了handleWorks()的槽,将worksSignal和handleWorks连接起来的方式有:

    1. Qt::AutoConnection - (默认)如果消息对象和槽对象关联在同一线程下,则使用Qt::DirectConnection方式;否则的话,像MainWindow和Worker两个关联在不同线程的对象,将采用Qt::QueuedConnection的方式。

    2. QT::DirectConnection - 发送消息的时候将直接调用槽对象的槽方法。注意这里的槽方法是在发送消息的线程上执行的,如果该槽方法是非线程安全的话会有问题的。

    3. Qt::QueuedConnection - 发送线程在发送消息后将继续执行,槽对象关联的线程在事件循环时会检测到该消息,并调用相应的槽方法。

    4. Qt::BlockingQueuedConnection - 在主线程发送worksSignal消息后,将阻塞直到在工作线程检测到该消息并运行worker->handleWorks()后恢复。

    5. Qt::UniqueConnection - 可以和上面4个方式联并(或操作),提示该连接是独一的。提示不能有相同的连接(消息对象和槽对象,消息和槽都相同)出现。

    这里特别提醒读者,一般地我们不建议将QThread对象moveToThread到它运行的线程上。原因是QThread是设计成一个管理线程的类,我们不应该在工作线程上管理工作线程,对吧。关于更多的技术细节,我不想多讲了,因为本系列的博文旨在共享经验技巧,而非翻译一些文档。

    在项目中,我都是通过继承QThread类实现后台进程的,通过重写run()函数填入线程需要运行的任务。上一篇博文中,我通过在QThread子类上嵌入InThreadObject对象快速实现线程通信的功能,请回顾QT高级编程技巧(一)-- 编写高效的signal & slot通信代码。还有下面一个技巧实现工作线程上的timer事件处理:

    void WorkerThread::run()
    {
      QTimer tmr;      // 关联在本线程上的QObject对象
      connect(&tmr, &QTimer::timeout, [=](){
        doSomething();
      });
      tmr.start(500);  // 500毫秒计时
      exec();          // 进入事件循环
    }

    关于多线线程的编程谈论到此,望能起抛砖引玉的效果。在实际的项目,可以参考下面的文档设计多线程或并发的应用:

    * QtConcurrent: http://doc.qt.io/qt-5/qtconcurrent.html

    * Thread Support in Qt: http://doc.qt.io/qt-5/threads.html

    转自:http://www.cnblogs.com/wenris/p/4450643.html

  • 相关阅读:
    springboot解析带控制字符\"的json字符串
    SpringBoot使用JSR356 Websocket使用configurator = SpringConfigurator.class自动注入Spring IOC的bean报错: Failed to find the root WebApplicationContext. Was ContextLoaderListener not used?
    REST API 调用新方法
    mac终端提示zsh: operation not permitted:怎么办?
    (6.1)分栏布局
    (6.2)弹性布局
    比较器 Comparison 与 IComparer
    requireJS 的回顾
    分享刚出炉的基于Blazor技术的Web应用开发框架
    架构必备技能第一谈
  • 原文地址:https://www.cnblogs.com/ITGUANCHAZHE/p/14319739.html
Copyright © 2020-2023  润新知