首先,寫個線程類,繼承自QThread,該線程做的事情很簡單:每兩秒打印一次自己的線程id,由於我對Qt的console打印函數不太瞭解,這裏還是使用c++的cout!
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <iostream>
using namespace std;
class Mythread : public QThread
{
Q_OBJECT
public:
explicit Mythread(QObject *parent = 0);
signals:
public slots:
protected:
void run();
};
#endif // MYTHREAD_H
#include "mythread.h"
Mythread::Mythread(QObject *parent) :
QThread(parent)
{
}
void Mythread::run()
{
while(1)
{
cout << "thread id: " << QThread::currentThreadId() << endl;
sleep(2);
}
}
#include <QtCore/QCoreApplication>
#include "mythread.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
cout << "main thread id:" << QThread::currentThreadId() << endl;
Mythread thread;
thread.start();
return a.exec();
}
這是一個Qt console進程,好了,運行一下,每隔兩秒打印一次子線程id。
接下來,我們給線程設置定時器,來替換sleep
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QTimer>
#include <iostream>
using namespace std;
class Mythread : public QThread
{
Q_OBJECT
public:
explicit Mythread(QObject *parent = 0);
signals:
public slots:
void mytimedout();
protected:
void run();
QTimer _timer;
};
#endif // MYTHREAD_H
#include "mythread.h"
Mythread::Mythread(QObject *parent) :
QThread(parent)
{
}
void Mythread::run()
{
connect(&_timer, SIGNAL(timeout()), this, SLOT(mytimedout()));
cout << "child thread id: " << QThread::currentThreadId() << endl;
_timer.start(2000);
exec();
}
void Mythread::mytimedout()
{
cout << "thread id: " << QThread::currentThreadId() << endl;
}
main與以前相同
發現並不能正常隔兩秒打印一次。根據我查的資料,大概原因是:QTimer是在主線程裏創建的,它發的信號只能與主線程相關的QObject來處理。詳細信息還需要細看文檔。
修改代碼,我們在子線程裏創建一個QTimer,並用它來發超時信號,子線程會阻塞在exec()這裏,所以不用擔心mytimer的生存期
將run改爲如下:
void Mythread::run()
{
QTimer mytimer;
connect(&mytimer, SIGNAL(timeout()), this, SLOT(mytimedout()));
cout << "child thread id: " << QThread::currentThreadId() << endl;
mytimer.start(2000);
exec();
}
此時可以打印了,不過,我們發現打印的時候,顯示的是主線程id,也就是說,mytimedout這個函數是在主線程裏執行的。根據所查資料,這裏大概是因爲:工作線程在創建的時候,線程的事件處理,是依附在創建者身上的,我們需要在創建之後,調用QObject::moveToThread()來改變依附性,
main修改如下:
#include <QtCore/QCoreApplication>
#include "mythread.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
cout << "main thread id:" << QThread::currentThreadId() << endl;
Mythread thread;
thread.start();
thread.moveToThread(&thread);
return a.exec();
}
這時候,便由子線程自己來打印了
接下來看下事件,QEvent!我們讓子線程往主線程發送QEvent,主線程接受到後,打印信息表示接收到了。
要讓主線程可以處理自定義事件,需要從QCoreApplication繼承一個類,並改寫虛函數event(QEvent *eve)
#ifndef MYAPP_H
#define MYAPP_H
#include <QCoreApplication>
#include <mythread.h>
class Myapp : public QCoreApplication
{
Q_OBJECT
public:
explicit Myapp(int argc, char* argv[], QObject *parent = 0);
bool event( QEvent * e );
signals:
public slots:
};
#endif // MYAPP_H
#include "myapp.h"
Myapp::Myapp(int argc, char* argv[], QObject *parent) :
QCoreApplication(argc, argv)
{
}
bool Myapp::event( QEvent * e )
{
if(e->type() == (mytype1)){
cout << "thread id:" << QThread::currentThreadId() << "recv a event, type is:" << e->type() << endl;
}
return QCoreApplication::event(e);
}
子線程裏,發送事件
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QTimer>
#include <QEvent>
#include <iostream>
using namespace std;
enum Type{ mytype1 = QEvent::User+1, mytype2 }; // 這是我自定義的事件類型。這裏有個問題,可能是qt的bug,如果我編譯成功了,然後改mytype1=QEvent::User,再編譯運行,就會收不到事件
class Mythread : public QThread
{
Q_OBJECT
public:
explicit Mythread(QObject *parent = 0);
signals:
public slots:
void mytimedout();
protected:
void run();
QTimer _timer;
};
#endif // MYTHREAD_H
#include <QCoreApplication>
#include "mythread.h"
Mythread::Mythread(QObject *parent) :
QThread(parent)
{
}
void Mythread::run()
{
QTimer mytimer;
connect(&mytimer, SIGNAL(timeout()), this, SLOT(mytimedout()));
cout << "child thread id: " << QThread::currentThreadId() << endl;
mytimer.start(2000);
exec();
}
void Mythread::mytimedout()
{
cout << "thread id: " << QThread::currentThreadId() << endl;
QEvent *eve = new QEvent((QEvent::Type)(mytype1));
QCoreApplication::postEvent(QCoreApplication::instance(), eve);
}
#include <QtCore/QCoreApplication>
#include "mythread.h"
#include "myapp.h"
int main(int argc, char *argv[])
{
Myapp a(argc, argv);
cout << "reg value:" << QEvent::registerEventType(mytype1) << endl; // 這裏註冊一下,看該類型的事件是否被別人註冊過了,避免混淆
cout << "reg value:" << QEvent::registerEventType(mytype2) << endl;
cout << "main thread id:" << QThread::currentThreadId() << endl;
Mythread thread;
thread.start();
thread.moveToThread(&thread);
return a.exec();
}
我們在Mythread::mytimedout()裏new 了一個QEvnet對象,卻沒有去delete它,那麼它會內存泄漏嗎?其實不會,因爲這個事件被發出去後,處理該事件的對象在處理完該事件後,會delete它,比如這裏是
bool Myapp::event( QEvent * e )
{
if(e->type() == (mytype1)){
cout << "thread id:" << QThread::currentThreadId() << "recv a event, type is:" << e->type() << endl;
}
return QCoreApplication::event(e);
}
return QCoreApplication::event(e); 這裏是轉給父類去處理,估計最終會由QObject的event函數來delete掉。
我們可以自己寫個類繼承自QEvent,QEvent的析構函數爲虛。我們的類繼承它,別人delete這個類時,除了會執行自己的析構函數以外,也會調用QEvent的析構函數。
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QTimer>
#include <QEvent>
#include <iostream>
using namespace std;
enum Type{ mytype1 = QEvent::User+1, mytype2 };
class Myevent:public QEvent
{
public:
Myevent(QEvent::Type type):QEvent(type){}
~Myevent(){
cout << "thread id: " << QThread::currentThreadId() << "destroy event, type is:" << type() << endl;
}
};
class Mythread : public QThread
{
Q_OBJECT
public:
explicit Mythread(QObject *parent = 0);
signals:
public slots:
void mytimedout();
protected:
void run();
QTimer _timer;
};
#endif // MYTHREAD_H
void Mythread::mytimedout()
{
cout << "thread id: " << QThread::currentThreadId() << endl;
QEvent *eve = new Myevent((QEvent::Type)(mytype1));
QCoreApplication::postEvent(QCoreApplication::instance(), eve);
}
可以看到Myapp的線程在處理完事件後,delete掉了事件,因爲調用了Myevent的析構函數。
最後,我們讓父類接收到mytype1事件後,往子線程發一個mytype2事件。
類Mythread需要重載event方法
bool Mythread::event( QEvent * e )
{
switch(e->type())
{
case mytype2:
cout << "child thread:" << QThread::currentThreadId() << "recv a event, type is:" << e->type() << endl;
break;
default:
break;
}
return QThread::event(e);
}
Myapp的event變爲
bool Myapp::event( QEvent * e )
{
if(e->type() == (mytype1)){
cout << "thread id:" << QThread::currentThreadId() << "recv a event, type is:" << e->type() << endl;
QEvent *eve = new Myevent((QEvent::Type)mytype2);
postEvent(_recv, eve);
}
return QCoreApplication::event(e);
}
這裏_recv是子線程的QObject指針
void setReceive(QObject *obj){
_recv = obj;
}
protected:
QObject *_recv;
main函數
#include <QtCore/QCoreApplication>
#include "mythread.h"
#include "myapp.h"
int main(int argc, char *argv[])
{
Myapp a(argc, argv);
cout << "reg value:" << QEvent::registerEventType(mytype1) << endl;
cout << "reg value:" << QEvent::registerEventType(mytype2) << endl;
cout << "main thread id:" << QThread::currentThreadId() << endl;
Mythread thread;
a.setReceive(&thread);
thread.start();
thread.moveToThread(&thread);
return a.exec();
}
這樣,子線程每2秒處理一個超時信號,打印相關信息和往主線程發事件mytype1,主線程接收到該事件後,往子線程發一個mytype2的事件。子線程處理mytype2事件,打印信息
注意:
thread.moveToThread(&thread) // 這不是一個好的方法,這裏作爲例子這麼用可以更快速讓例子運行起來
參考:
http://www.qtcn.org/bbs/simple/?t32303.html
http://www.cnblogs.com/tankery/archive/2011/03/09/2004561.html
http://www.cnblogs.com/andreitang/archive/2011/08/03/2125815.html