zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    算法竞赛入门经典习题2-3 韩信点兵
    ios入门之c语言篇——基本函数——5——素数判断
    ios入门之c语言篇——基本函数——4——数值交换函数
    144. Binary Tree Preorder Traversal
    143. Reorder List
    142. Linked List Cycle II
    139. Word Break
    138. Copy List with Random Pointer
    137. Single Number II
    135. Candy
  • 原文地址:https://www.cnblogs.com/chinasoft/p/15226028.html
Copyright © 2011-2022 走看看