zoukankan      html  css  js  c++  java
  • Qt5中运行后台网络读取线程与主UI线程互交

    项目中有一个需求就是,因为需要请求服务端数据,因为网络的读取会阻塞,所以该过程不能放在Qt中的UI主线程当中,需要用一个后台线程来读取数据,数据准备完毕后

    在通过Qt5中的信号槽机制来跨线程的传递数据。之前的博文使用过moveToThread的方式来讲解创建后台线程,但是现在后台线程需要与前台UI线程数据互交,然而,最悲剧的就是信号发出去了,

    但是前台的UI线程对象收不到信号,也就是相应的槽函数没被调用。之前博文后台线程是没有与前台UI线程互交的,因为它是采集数据的线程,只管往目标地址发送数据就可以了。但是接收线程就不一样了,

    它需要把后台接收到的网络数据放到前台GUI中展现出来。这不可避免的产生互交和数据的传递。

    前台的UI线程创建后台线程的代码大概如下:

     1  RecvDataObject *recv_obj = new RecvDataObject;
     2 
     3     QThread* backgroundRecvThread = new QThread;
     4 
     5    
     6      recv_obj->moveToThread(backgroundRecvThread);
     7     
     8     connect(recv_obj, &RecvDataObject::dataRecved,
     9         this, &TerminalStatusWidget::slotDataRecved,Qt::QueuedConnection);
    10    
    11    
    12     backgroundRecvThread->start();

    注意,多线程间的信号槽传递,在connect的时候需要以Qt::QueuedConnection的方式,不然以Qt::DirectConnection的方式接收者UI线程会很长时间收不到后台线程发出的信号,或者信号直接丢失都是有可能的。参考

    http://www.qtcentre.org/threads/17764-emit-qt-signal-is-very-slow-how-it-can-be-optimized

    RecvDataObect是用来接收后台数据的对象被move到了backgroundRecvThread线程中去执行了。其声明是这样的:

     1 class RecvDataObject : public QObject
     2 {
     3     Q_OBJECT
     4 
     5 public:
     6     RecvDataObject();
     7     ~RecvDataObject();
     8 signals:
     9     void dataRecved(std::vector<RunTimeInfo> list);
    10 public slots:
    11     void slotRecvTask();
    12 private:
    13     QTimer m_RecvTask;
    14 
    15 };

    该类的构造函数我采用了一个Timer来循环执行slotRecvTask()的任务,专门创建网络连接,接收网络数据。然后数据接收完毕后,通过发送dataRecved的信号传递到UI主线程中的slot函数中,但是不能正常工作。槽函数一直不能调用。

    上网查了原因才知道,原来Qt的信号槽函数只默认支持Qt的类型和C++提供的内建的基本类型,比如int double float啥的,根本不支持C++的std::string std::vector 自定义的struct类型。所以需要用Qt提供的Q_DECLARE_METATYPE和

    qRegisterMetaType来声明和注册自定义的类型和C++的其他类型。  所以以上的C++类RecvDataObject应该变成以下:

     1 Q_DECLARE_METATYPE(RunTimeInfo)
     2 Q_DECLARE_METATYPE(std::vector<RunTimeInfo>)
     3 
     4 class RecvDataObject : public QObject
     5 {
     6     Q_OBJECT
     7 
     8 public:
     9     RecvDataObject()
    10    {
    11         qRegisterMetaType<RunTimeInfo>("RunTimeInfo");
    12         qRegisterMetaType<std::vector<RunTimeInfo>>("std::vector<RunTimeInfo>");
    13     
    14     m_RecvTask.setInterval(5000);
    15     connect(&m_RecvTask, SIGNAL(timeout()), this, SLOT(slotRecvTask()));
    16     m_RecvTask.start();
    17    }
    18     ~RecvDataObject();
    19 signals:
    20     void dataRecved(std::vector<RunTimeInfo> list);
    21 public slots:
    22     void slotRecvTask();
    23 private:
    24     QTimer m_RecvTask;
    25 
    26 };

    然后主线程的Widget类的构造函数里面还必须加入:

    1 qRegisterMetaType<RunTimeInfo>("RunTimeInfo");
    2 qRegisterMetaType<std::vector<RunTimeInfo>>("std::vector<RunTimeInfo>");

    这样信号槽函数才能正确工作,通过信号槽机制跨线程的数据传递完成了,完美运行。

    references:

    https://stackoverflow.com/questions/638251/how-to-emit-cross-thread-signal-in-qt

    http://www.qtcentre.org/threads/54409-signal-slot-with-std-string-How

    https://stackoverflow.com/questions/14083599/signals-and-slots-passing-data

  • 相关阅读:
    odbc 连接字符串
    25个国外优秀电子商务网站设计案例
    用css 添加手状样式,鼠标移上去变小手,变小手
    js如何获得FCKeditor控件的值
    导致Asp.Net站点重启的10个原因
    分享45款高质量的免费(X)HTML/CSS模板
    20110627 VisualSVN安装与配置(Delphi72010/VS2010)
    iBatis把一个表的sqlmap配置的多个xml中。
    ASP.NET State Service
    存储过程分页
  • 原文地址:https://www.cnblogs.com/foohack/p/7526267.html
Copyright © 2011-2022 走看看