对qt多线程以及事件投递的理解

转载 2016年05月30日 10:20:15

原文:http://blog.csdn.net/shaochat/article/details/41956707?utm_source=tuicool&utm_medium=referral

用QThread启动线程的良好方法以及理解。

1) 用原始的QThread的started信号触发自定义的slot启动线程,而不是派生QThread的类重载run函数启动线程。
将一个类派生自QObject,然后实现所有的signal/slot,然后通过调用movetothread函数来使他们执行在新的线程里面,而不是每次都要重新派生QThread,并且派生QThread的另外一个不好的地方是只有run函数内部的代码才会执行在新线程里面,相比起来,派生QObject并使用movetothread函数更具有灵活性。

2) 自定义对象moveToThread进线程后,事件循环可以完全在此线程中运行。而它的内存管理应该还属于主线程。
如下例子中(MyObject* myObj)被moveToThread进线程(QThread *thread)。假如对象MyObject* myObj)有父亲,它不能移动这种关系。在另一个线程(而不是创建它的那个线程)中delete QObject对象是不安全的。除非你可以保证在同一时刻对象不在处理事件。可以用QObject::deleteLater(),它会投递一个DeferredDelete事件,这会被对象线程的事件循环最终选取到。

3)exec的重要性。
主线程开始它的事件循环需要调用QCoreApplication::exec(), 子线程需要用QThread::exec(),功能基本是一样的,当然qt内部肯定是不同的实现。
假如没有事件循环运行,事件不会分发给对象。举例来说,假如你在一个线程中创建了一个QTimer对象,但从没有调用过exec(),那么QTimer就不会发射它的timeout()信号.对deleteLater()也不会工作。(这同样适用于主线程)。
4) 子线程接收事件
a) 如下例子中,子线程并不存在exec()函数。原因是exec()函数在QThread的run()中调用了。
我们这里并没有做一个QThread派生类,在run中调用exec()。因为我不推荐怎么干。原因很简单,并不需要这么干。进一步说明是:qt提供的QThread已经完全满足线程创建的需要。为什么还要自己再画蛇添足呢。
b)本例子真正使用的是QCoreApplication::postEvent,和重载QObject类的customEvent的方法。消息一样会在(MyObject* myObj)所在的线程处理。

5) 关于QCoreApplication::postEvent和QCoreApplication::sendEvent
postEvent: 可以给别的线程发送事件。事件会在目的对象所属的线程中运行。这是一个异步接口。
sendEvent: 仅用于同一个线程之间发送事件。目的对象必须与当前线程一样。这是一个同步接口。假如发送给属于另一个线程的对象,会报错:ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread a51f48. Receiver '' (of type
 'MyObject') was created in thread a3bf18", file kernel\qcoreapplication.cpp, line 539



以下是结合上述描述的3个文件。可正常编译运行。
-------------------------------------------------------------------------------------------------
maim.cpp
-------------------------------------------------------------------------------------------------

#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <QObject>
#include <QTimer>
#include "myobject.h"


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main:" << (qlonglong)QThread::currentThreadId();

    //! 业务逻辑对象
    MyObject* myObj = new MyObject;
    //! 线程
    QThread *thread = new QThread();
    //! 定时器
    QTimer *timer = new QTimer();

    //! 把业务逻辑对象设置进线程,不要把Qthread放进自己线程,这样的写法不建议。
    myObj->moveToThread(thread);

    //! 线程启动
    thread->start();

    //! 绑定的时候最好设置QueuedConnection。这样就允许在myObj线程执行,否则可能会在主线程执行。
    //! 如果希望不要再子线程执行,用DirectConnection。
    QObject::connect(thread, SIGNAL(started()), myObj, SLOT(Started()), Qt::QueuedConnection);
    QObject::connect(timer, SIGNAL(timeout()), myObj, SLOT(Timeout()), Qt::QueuedConnection);
    //! 定时器启动
    timer->start(1000);

    //! 发送事件
    QCoreApplication::postEvent(myObj, new QEvent(CustomEvent_Login));
    qDebug() << QString("[%1]send the event: %2!")
                .arg((qlonglong)QThread::currentThreadId())
                .arg(CustomEvent_Login);

    //! MyObject::Started/MyObject::Timeout/都会在同一个子线程调用。
    return a.exec();
}



-------------------------------------------------------------------------------------------------



myobject.cpp
-------------------------------------------------------------------------------------------------
#include "myobject.h"
#include <QDebug>
#include <QThread>

MyObject::MyObject()
{
}


void MyObject::Started()
{
    qDebug() << "Started:" << (qlonglong)QThread::currentThreadId() ;
}

void MyObject::Timeout()
{
    qDebug() << "Timeout:" << (qlonglong)QThread::currentThreadId() ;
}

void MyObject::customEvent(QEvent *e)
{
    if (e->type() == CustomEvent_Login) //捕获消息
    {

        qDebug() << QString("[%1]catch the event: %2!")
                       .arg((qlonglong)QThread::currentThreadId())
                       .arg(e->type());
    }
}




-------------------------------------------------------------------------------------------------
myobject.h
-------------------------------------------------------------------------------------------------
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>

#include <QEvent>


const QEvent::Type CustomEvent_Login = (QEvent::Type)5001; //! 建议用5000以上唯一的标识

class MyObject : public QObject
{
    Q_OBJECT
public:
    MyObject();

protected slots:
    void Started();
    void Timeout();

private:
    void customEvent(QEvent *e); //! 重载QObject的续虚函数

};

#endif // MYOBJECT_H

对qt多线程以及事件投递的理解

用QThread启动线程的良好方法以及理解。
  • shaochat
  • shaochat
  • 2014年12月16日 10:06
  • 2906

谈谈我对多线程的理解

一、提到多线程,就不得不理解以下几点: 1.程序,进程,线程这三者之间的关系? 简单来说,一程序可以调用多个进程,比如一个视频播放器程序,里面就存在两个进程:一个是播放视频的进程,一个是下载上传视频...
  • DongMeng1994
  • DongMeng1994
  • 2017年01月17日 20:17
  • 3696

Qt 多线程之逐线程事件循环

每个线程可以有它的事件循环,初始线程开始它的事件循环需使用QCoreApplication::exec(),别的线程开始它的事件循环需要用QThread::exec().像QCoreApplicati...
  • GZFStudy
  • GZFStudy
  • 2014年10月30日 20:34
  • 1505

对于java多线程的理解(一)

工作到现在3年了,一直没有接触到线程的相关应用,因为java web服务器已经很好的把线程封装好了,无需再对线程进行进一步的处理了,但是有的时候,关于线程的相关知识还是需要知道的,因为万恶的面试官为了...
  • liuxinnidongde
  • liuxinnidongde
  • 2016年11月14日 13:50
  • 742

我理解的多进程和多线程

对多进程和多线程,大家都有所了解,但却都很难说清楚多进程和多线程,工作中又如何去运用,特总结如下...
  • swjtuwyp
  • swjtuwyp
  • 2016年05月21日 11:23
  • 3819

对WEB标准以及W3C的理解与认识?

web标准简单来说可以分为结构、表现和行为。其中结构主要是有HTML标签组成。或许通俗点说,在页面body里面我们写入的标签都是为了页面的结构。表现即指css样式表,通过css可以是页面的结构标签更具...
  • liang6678
  • liang6678
  • 2016年07月04日 23:36
  • 11312

对于restful的简单理解

REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移。它首次出现在2000年Roy Fielding的博士论文中,Roy Fi...
  • Poison__biting
  • Poison__biting
  • 2017年08月10日 19:41
  • 509

事件驱动的单线程模型 VS 多线程模型

ThreadPerConnection的多线程模型 优点:简单易用,效率也不错。在这种模型中,开发者使用同步操作来编写程序,比如使用阻塞型I/O。使用同步操作的程序能够隐式地在线程的运行堆栈中维...
  • jmxyandy
  • jmxyandy
  • 2012年03月10日 05:33
  • 2413

对事物的理解

面试过程中,主考官问道:谈谈你对事物的理解,感觉当时回答的好没有连贯性。事后总结如下: 谈谈我对事物的理解,从如下几个方面进行剖析: 一、事物的特性 二、对事物产生的原因 三、如何...
  • fengchao2016
  • fengchao2016
  • 2017年04月17日 07:22
  • 707

对WEB标准以及W3C的理解与认识

在写代码的时候应该注意:1.标签闭合 2.标签小写 3.不能随意嵌套提高被搜索引擎搜到几率:mate中的name变量【其中keywords和description尤其重要】Meta name=”K...
  • yoany
  • yoany
  • 2015年06月29日 10:30
  • 6511
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:对qt多线程以及事件投递的理解
举报原因:
原因补充:

(最多只允许输入30个字)