Qt4.7中,執行緒,訊號,事件的一點理解
阿新 • • 發佈:2019-02-11
這幾天在學執行緒,覺得不錯就轉載一下。
首先,寫個執行緒類,繼承自QThread,該執行緒做的事情很簡單:每兩秒列印一次自己的執行緒id,由於我對Qt的console列印函式不太瞭解,這裡還是使用c++的cout!
[cpp] view plaincopyprint?- #ifndef MYTHREAD_H
- #define MYTHREAD_H
- #include <QThread>
- #include <iostream>
- usingnamespace std;
- class Mythread : public QThread
- {
- Q_OBJECT
- public:
- explicit Mythread(QObject *parent = 0);
- signals:
- public slots:
- protected:
- void run();
- };
- #endif // MYTHREAD_H
[cpp] view plaincopyprint?#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 "mythread.h" Mythread::Mythread(QObject *parent) : QThread(parent) { } void Mythread::run() { while(1) { cout << "thread id: " << QThread::currentThreadId() << endl; sleep(2); } }
[cpp] view plaincopyprint?
- #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();
- }
#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
[cpp] view plaincopyprint?- #ifndef MYTHREAD_H
- #define MYTHREAD_H
- #include <QThread>
- #include <QTimer>
- #include <iostream>
- usingnamespace 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
#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
[cpp] view plaincopyprint?
- #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;
- }
#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改為如下:
[cpp] view plaincopyprint?- 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::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修改如下:
[cpp] view plaincopyprint?- #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();
- }
#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)
[cpp] view plaincopyprint?- #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
#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
[cpp] view plaincopyprint?
- #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);
- }
#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);
}
子執行緒裡,傳送事件 [cpp] view plaincopyprint?
- #ifndef MYTHREAD_H
- #define MYTHREAD_H
- #include <QThread>
- #include <QTimer>
- #include <QEvent>
- #include <iostream>
- usingnamespace 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
#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
[cpp] view plaincopyprint?
- #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 <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);
}
[cpp] view plaincopyprint?
- #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();
- }
#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它,比如這裡是
[cpp] view plaincopyprint?- 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);
- }
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>
- usingnamespace 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
#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
[cpp] view plaincopyprint?
- void Mythread::mytimedout()
- {
- cout << "thread id: " << QThread::currentThreadId() << endl;
- QEvent *eve = new Myevent((QEvent::Type)(mytype1));
- QCoreApplication::postEvent(QCoreApplication::instance(), eve);
- }
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方法
[cpp] view plaincopyprint?- 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);
- }
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變為 [cpp] view plaincopyprint?
- 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);
- }
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指標 [cpp] view plaincopyprint?
- void setReceive(QObject *obj){
- _recv = obj;
- }
- protected:
- QObject *_recv;
void setReceive(QObject *obj){
_recv = obj;
}
protected:
QObject *_recv;
main函式 [cpp] view plaincopyprint?
- #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();
- }
#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事件,列印資訊
注意:
[cpp] view plaincopyprint?- thread.moveToThread(&thread) // 這不是一個好的方法,這裡作為例子這麼用可以更快速讓例子執行起來
thread.moveToThread(&thread) // 這不是一個好的方法,這裡作為例子這麼用可以更快速讓例子執行起來
參考: