qt多线程编程之使用moveToThread

  • Qt有两种多线程的方法,其中一种是继承QThread的run函数,另外一种是把一个继承于QObject的类用moveToThread函数转移到一个QThread对象里。 Qt4.8之前都是使用继承QThread的run这种方法,但是Qt4.8之后,Qt官方建议使用第二种方法。
  • 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 53
//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();
    myObj->moveToThread(thread);	//! 把业务逻辑对象设置进线程,不要把Qthread放进自己线程,这样的写法不建议。
    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);
    return a.exec();
}
//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:
    virtual void customEvent(QEvent *e); //重载可用于接收处理用户自定义的事件
};
#endif // MYOBJECT_H
//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());
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值