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

1、使用QObject子类的movetothread方法 代替 QThread子类的run 实现多线程。
使用原始QThread对象的start方法启动线程==>连接原始QThread对象的started信号到QObject子类的槽(线程部分)
 
2、有parent的object是不能moveToThread被移动到其他线程中的,所以还需要把子类对象给setParent(NULL)一下再moveToThread。
 
3、QObject子类对象在movetothread后,不要在线程中进行delete,因为QObject子类对象的内存管理还属于主线程。
 
4、QCoreApplication::exec()开启了主线程的事件循环。子线程也是一样,需要通过调用exec开启事件循环,否则事件不会分发给对对象。
QThread线程并不存在exec()函数。原因是exec()函数在QThread的run()中已经调用了。
 
5、postEvent: 可以给别的线程发送事件。事件会在目的对象所属的线程中运行。异步接口。
sendEvent: 仅用于同一个线程之间发送事件。目的对象必须与当前线程一样。同步接口
 
6、跨线程的事件传递可以通过 QCoreApplication::postEvent重载QObject类的customEvent的方法 实现。
 
--------------------------------------------------------------------------------------------------------------------
 
1) 用原始的QThreadstarted信号触发自定义的slot启动线程(需要connect这个QThread对象的started信号)
而不是派生QThread的类重载run函数启动线程。
将一个类派生自QObject,然后实现所有的signal/slot,然后通过调用movetothread函数来使他们执行在新的线程里面,
而不是每次都要重新派生QThread,并且派生QThread的另外一个不好的地方是只有run函数内部的代码才会执行在新线程里面,
相比起来,派生QObject并使用movetothread函数更具有灵活性
 
2) 自定义对象(QObject子类)moveToThread进线程后,事件循环可以完全在此线程中运行。而它的内存管理应该还属于主线程
有parent的object是不能moveToThread被移动到其他线程中的,所以还需要把子类对象给setParent(NULL)一下再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
 
代码片段如下:(完整例子请到转载地址查看)
const QEvent::Type CustomEvent_Login = ( QEvent::Type )5001; //! 建议用5000以上唯一的标识

void MyObject::customEvent( QEvent *e )
{
    if ( e->type() == CustomEvent_Login ) //捕获消息
    {
        qDebug() << QString( "[%1]catch the event: %2!" )
                 .arg( ( qlonglong )QThread::currentThreadId() )
                 .arg( e->type() );
    }
}

 

int main( int argc, char *argv[] )
{
    QCoreApplication a( argc, argv );
    qDebug() << "main:" << ( qlonglong )QThread::currentThreadId();

    //把业务逻辑对象设置进线程,不要把Qthread放进自己线程,这样的写法不建议。
    //绑定的时候最好设置QueuedConnection。这样就允许在myObj线程执行,否则可能会在主线程执行。
    //如果希望不要再子线程执行,用DirectConnection。
    MyObject *myObj = new MyObject;
    QThread *thread = new QThread();
    myObj->moveToThread( thread );
    thread->start();
    QObject::connect( thread, SIGNAL( started() ), myObj, SLOT( OnStartedSlot() ), Qt::QueuedConnection );

    //定时器
    //MyObject::Started/MyObject::Timeout/都会在同一个子线程调用。
    QTimer *timer = new QTimer();
    QObject::connect( timer, SIGNAL( timeout() ), myObj, SLOT( OnTimeoutSlot() ), 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 );

    return a.exec();
}

 
转载地址:http://blog.csdn.net/shaochat/article/details/41956707?utm_source=tuicool
http://blog.csdn.net/sonydvd123/article/details/8660624
 
 

posted on 2016-01-16 17:16  cthu  阅读(3950)  评论(0编辑  收藏  举报

导航