对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启动线程的良好方法以及理解。

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

每个线程可以有它的事件循环,初始线程开始它的事件循环需使用QCoreApplication::exec(),别的线程开始它的事件循环需要用QThread::exec().像QCoreApplicati...

qt学习四部曲(Console;QByteArray;模态对话框;多线程及事件通信 )

如何在Console中使用cin、cout、cerr; QByteArray显示中文; 模态对话框和非模态对话框; 多线程及通过事件进行通信 Qt学习系列之一:如何在Console中使用cin...

理解Qt多线程类(转)

POINT 1:QThread类的实例与普通类的实例没什么不同,只是运行着的run()函数会不同 例1:class MThread :public QThread{public:    MThread...
  • lbsljn
  • lbsljn
  • 2011年03月31日 10:35
  • 895

C#基于异步事件回调多线程容器

  • 2013年07月25日 16:24
  • 1.56MB
  • 下载

基于事件的 NIO 多线程服务器

  • 2012年09月27日 15:16
  • 744KB
  • 下载

理解Qt多线程类

POINT 1:QThread类的实例与普通类的实例没什么不同,只是运行着的run()函数会不同 例1: 1.class MThread :public QThread 2.{ 3.pub...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:对qt多线程以及事件投递的理解
举报原因:
原因补充:

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