QT首先为我们提供了信号和槽的机制,且该机制原生支持跨线程。假设我们在16核心服务器上,则使用 15个 QThread对象管理15组工作线程(留一个给主界面)。但是,如果仔细看了QT的文档,就会发现QThread的信号事件循环默认是在创建者中(很多时候就是主线程!),所以,要想让槽在子线程运行,一般是派生一个QObject的类,并把对象MoveToThread到某个QThread管理的线程上去。这样,信号和槽就是全异步FIFO了。其次,QT提供了引用计数的QByteArray封装,这个东西在参数传递的时候,速度很快,很少出现memcpy,生存期也特别容易控制。虽然C++11里有 shared_ptr<T>,但是那个东西还是需要在一开始new 一个int8型的存储区,很讨厌。
之前我参考了一些代码,都是在主线程中声明QThread后,然后直接调用start()函数,这样的话根本没有起到多线程的功能,因为都是将QThread类化以后,在主线程中执行的。要想将费事的操作另外开一个线程。可以使用QObject对象,然后使用moveToThread(),最后使用类化的QObject的对象调用start()方法就可以了。
>QThread mthread; myobject object;//myobject继承QThread object.DoWork(mthread); object.moveToThread(&mthread); mthread.start();
.cpp
void myobject::DoWork(QThread &thread)//关联线程与SLOT函数,thread一启动,就执行SLOT函数。 { connect(&thread,SIGNAL(started()),this,SLOT(dododo())); } void myobject::dododo()//SLOT 函数 { for(int i = 0 ;i<10;i++) { qDebug() << i; } }
参考以下文章编写:
http://blog.csdn.net/imhikaru/article/details/6635095
QThread类并不是代表一个新的线程,而是QT提供的一个接口,用于控制一个子线程。每个QThread的实例就代表着对一个新线程的一个控制类。对于第一次使用QT多线程的人,或许就会很迷惑很不适应。
QThread提供一个公共槽接口–start(),当你有一个QThread的实例例如
QThread q_thread
当你调用q_thread.start()时,q_thread控制的子线程就会开始执行,执行的入口就是QThread类的virtual protected 函数 QThread::run(),此时,run()里面的语句都是在新起的一个线程里执行,默认QT自带的run()实现就是QThread::exec(),表示开始QThread类的消息循环。exec()执行后,q_thread就能接受到信号(如果有连接信号的话),执行响应的槽。
重点来了,run()里面的语句是在新起的线程执行的,而q_thread收到信号,执行相应槽里面的语句,却是由QThread所在的线程执行的。包括继承QThread类的子类以及其信号槽。
例如
一个QThread的子类:
class MyThread : public QThread { Q_OBJECT public: MyThread(); protected: virtual void run (); public slots: void MyThreadSlot(); signals: void MyThreadSignal(); }; void MyThread::run() { int i = 0; i++; exec(); }
一个主对话框类,里面声明一个MyThread变量
class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); private: MyThread q_myThread; public slots: void MainWindowSlot(); signals: void MainWindowSignal(); };
当我们在MainWindow里调用q_myThread.start()时, MyThread::run()开始执行,假设主线程是Thread1,新起的线程是Thread2,
int i = 0;i++;都是在Thread2里执行。接着exec()后,MyThread的事件循环开始了。
我们先
connect(this,SIGNAL(MainWindowSigna()),&q_myThread,SLOT(MyThreadSlot()),Qt::QueuedConnection);
当MainWindow里 emit MainWindowSignal()时,MyThread的MyThreadSlot()会执行,你会发现,MyThreadSlot()执行的线程并不是我们想的Thread2,而是Thread1,因为q_thread在MainWindow中,MainWindow就在主线程Thread1中。那么我们如何才能在让我们写的代码在Thread2执行呢,其中一个方法便是改写run(),函数。
void MyThread::run() { while(1) { do_something(); } }
当我们调用q_myThread.start()时,do_something()就会循环执行,并且在新的线程Thread2中。而MainWindow的消息循环继续在Thread1里执行。总算有点像Windows的API CreateThread()了,但是如何给他传参数,在MyThread()的参数表里加?可以,那么如何与Thread1同步?在参数里加个用于同步的锁的地址?这样两个类之间的耦合度又大大增加,MyThread类几乎是不可重用的了。以前在Windows写多线程,最多就写个函数和它的参数的结构体,现在在QT还得子类化QThread,而且得到一个结构奇差的多线程结构。可真麻烦。
既然使用了QT,当然好好利用QT的信号槽机制,使得各类设计上比较独立,提高可重用性。一个更好的方法是利用QObject::moveToThread(QThread*)函数。前面我们说过,QThread的槽函数会在QThread类所在的线程执行,所以我们不应该子类化QThread,因为子类化之后它的槽函数依然会在MainWindow所在线程执行,往往不是我们所想要的。除非你想进入改写run()函数这个恶梦。
我们声明一个新的类,继承QObject,因为QOject可以使用信号槽。更重要的因为moveToThread()就是QObject的成员函数。
class MyObject : public QObject { Q_OBJECT public: MyObject(); public slots: void MyObjectSlot(); signals: void MyObjectSignal(); };
在MainWindow类中声明 MyObject q_myObj 在构造函数里做如下处理 connect(this,SIGNAL(MainWindowSignal());q_myObj,SLOT(MyObjectSlot()),Qt::QueuedConnection); q_myObj.moveToThread(&q_myThread);
当我们发送一个信号MainWindowSignal()信号时,发现MyObjectSlot()是在新线程Thread2里执行的,不妨在MyObjectSlot()里sleep1分钟,MainWindow还是能响应的。
至于MyObject的设计就随个人喜好了。可以把需要用到新线程的工作封装到MyObject的槽函数里,并且可以通过信号来通知MyObject开始或停止工作,而且还可以传参数。设计上比重载run()要好得多。
总结,要使用多线程,先继承QObject,在里面实现需要多线程运行的语句。然后在主线程里声明一个QThread对象,把自己实现的Obj moveToThread移动到新线程里, QThread::start()开始运行。