1. 程式人生 > >Qt4.7中,執行緒,訊號,事件的一點理解

Qt4.7中,執行緒,訊號,事件的一點理解

這幾天在學執行緒,覺得不錯就轉載一下。

首先,寫個執行緒類,繼承自QThread,該執行緒做的事情很簡單:每兩秒列印一次自己的執行緒id,由於我對Qt的console列印函式不太瞭解,這裡還是使用c++的cout!

[cpp] view plaincopyprint?
  1. #ifndef MYTHREAD_H
  2. #define MYTHREAD_H
  3. #include <QThread>
  4. #include <iostream>
  5. usingnamespace std;  
  6. class Mythread : public QThread  
  7. {  
  8.     Q_OBJECT  
  9. public:  
  10. explicit Mythread(QObject *parent = 0);  
  11. signals:  
  12. public slots:  
  13. protected:  
  14. void run();  
  15. };  
  16. #endif // MYTHREAD_H
#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
[cpp] view plaincopyprint?
  1. #include "mythread.h"
  2. Mythread::Mythread(QObject *parent) :  
  3.     QThread(parent)  
  4. {  
  5. }  
  6. void Mythread::run()  
  7. {  
  8. while(1)  
  9.     {  
  10.         cout << "thread id: " << QThread::currentThreadId() << endl;  
  11.         sleep(2);  
  12.     }  
  13. }  
#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?
  1. #include <QtCore/QCoreApplication>
  2. #include "mythread.h"
  3. int main(int argc, char *argv[])  
  4. {  
  5.     QCoreApplication a(argc, argv);  
  6.     cout << "main thread id:" << QThread::currentThreadId() << endl;  
  7.     Mythread thread;  
  8. thread.start();  
  9. return a.exec();  
  10. }  
#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?
  1. #ifndef MYTHREAD_H
  2. #define MYTHREAD_H
  3. #include <QThread>
  4. #include <QTimer>
  5. #include <iostream>
  6. usingnamespace std;  
  7. class Mythread : public QThread  
  8. {  
  9.     Q_OBJECT  
  10. public:  
  11. explicit Mythread(QObject *parent = 0);  
  12. signals:  
  13. public slots:  
  14. void mytimedout();  
  15. protected:  
  16. void run();  
  17.     QTimer _timer;  
  18. };  
  19. #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?
  1. #include "mythread.h"
  2. Mythread::Mythread(QObject *parent) :  
  3.     QThread(parent)  
  4. {  
  5. }  
  6. void Mythread::run()  
  7. {  
  8.     connect(&_timer, SIGNAL(timeout()), this, SLOT(mytimedout()));  
  9.     cout << "child thread id: " << QThread::currentThreadId() << endl;  
  10.     _timer.start(2000);  
  11.     exec();  
  12. }  
  13. void Mythread::mytimedout()  
  14. {  
  15.     cout << "thread id: " << QThread::currentThreadId() << endl;  
  16. }  
#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?
  1. void Mythread::run()  
  2. {  
  3.     QTimer mytimer;  
  4.     connect(&mytimer, SIGNAL(timeout()), this, SLOT(mytimedout()));  
  5.     cout << "child thread id: " << QThread::currentThreadId() << endl;  
  6.     mytimer.start(2000);  
  7.     exec();  
  8. }  
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?
  1. #include <QtCore/QCoreApplication>
  2. #include "mythread.h"
  3. int main(int argc, char *argv[])  
  4. {  
  5.     QCoreApplication a(argc, argv);  
  6.     cout << "main thread id:" << QThread::currentThreadId() << endl;  
  7.     Mythread thread;  
  8. thread.start();  
  9. thread.moveToThread(&thread);  
  10. return a.exec();  
  11. }  
#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?
  1. #ifndef MYAPP_H
  2. #define MYAPP_H
  3. #include <QCoreApplication>
  4. #include <mythread.h>
  5. class Myapp : public QCoreApplication  
  6. {  
  7.     Q_OBJECT  
  8. public:  
  9. explicit Myapp(int argc, char* argv[], QObject *parent = 0);  
  10. bool event( QEvent * e );  
  11. signals:  
  12. public slots:  
  13. };  
  14. #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?
  1. #include "myapp.h"
  2. Myapp::Myapp(int argc, char* argv[], QObject *parent) :  
  3.     QCoreApplication(argc, argv)  
  4. {  
  5. }  
  6. bool Myapp::event( QEvent * e )  
  7. {  
  8. if(e->type() == (mytype1)){  
  9.         cout << "thread id:" << QThread::currentThreadId() << "recv a event, type is:" << e->type() << endl;  
  10.     }  
  11. return QCoreApplication::event(e);  
  12. }  
#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?
  1. #ifndef MYTHREAD_H
  2. #define MYTHREAD_H
  3. #include <QThread>
  4. #include <QTimer>
  5. #include <QEvent>
  6. #include <iostream>
  7. usingnamespace std;  
  8. enum Type{ mytype1 = QEvent::User+1, mytype2 }; // 這是我自定義的事件型別。這裡有個問題,可能是qt的bug,如果我編譯成功了,然後改mytype1=QEvent::User,再編譯執行,就會收不到事件
  9. class Mythread : public QThread  
  10. {  
  11.     Q_OBJECT  
  12. public:  
  13. explicit Mythread(QObject *parent = 0);  
  14. signals:  
  15. public slots:  
  16. void mytimedout();  
  17. protected:  
  18. void run();  
  19.     QTimer _timer;  
  20. };  
  21. #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?
  1. #include <QCoreApplication>
  2. #include "mythread.h"
  3. Mythread::Mythread(QObject *parent) :  
  4.     QThread(parent)  
  5. {  
  6. }  
  7. void Mythread::run()  
  8. {  
  9.     QTimer mytimer;  
  10.     connect(&mytimer, SIGNAL(timeout()), this, SLOT(mytimedout()));  
  11.     cout << "child thread id: " << QThread::currentThreadId() << endl;  
  12.     mytimer.start(2000);  
  13.     exec();  
  14. }  
  15. void Mythread::mytimedout()  
  16. {  
  17.     cout << "thread id: " << QThread::currentThreadId() << endl;  
  18.     QEvent *eve = new QEvent((QEvent::Type)(mytype1));  
  19.     QCoreApplication::postEvent(QCoreApplication::instance(), eve);  
  20. }  
#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?
  1. #include <QtCore/QCoreApplication>
  2. #include "mythread.h"
  3. #include "myapp.h"
  4. int main(int argc, char *argv[])  
  5. {  
  6.     Myapp a(argc, argv);  
  7.     cout << "reg value:" << QEvent::registerEventType(mytype1) << endl; // 這裡註冊一下,看該型別的事件是否被別人註冊過了,避免混淆
  8.     cout << "reg value:" << QEvent::registerEventType(mytype2) << endl;  
  9.     cout << "main thread id:" << QThread::currentThreadId() << endl;  
  10.     Mythread thread;  
  11. thread.start();  
  12. thread.moveToThread(&thread);  
  13. return a.exec();  
  14. }  
#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?
  1. bool Myapp::event( QEvent * e )  
  2. {  
  3. if(e->type() == (mytype1)){  
  4.         cout << "thread id:" << QThread::currentThreadId() << "recv a event, type is:" << e->type() << endl;  
  5.     }  
  6. return QCoreApplication::event(e);  
  7. }  
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的解構函式。

[cpp] view plaincopyprint?
  1. #ifndef MYTHREAD_H
  2. #define MYTHREAD_H
  3. #include <QThread>
  4. #include <QTimer>
  5. #include <QEvent>
  6. #include <iostream>
  7. usingnamespace std;  
  8. enum Type{ mytype1 = QEvent::User+1, mytype2 };  
  9. class Myevent:public QEvent  
  10. {  
  11. public:  
  12.     Myevent(QEvent::Type type):QEvent(type){}  
  13.     ~Myevent(){  
  14.         cout << "thread id: " << QThread::currentThreadId() << "destroy event, type is:" << type() << endl;  
  15.     }  
  16. };  
  17. class Mythread : public QThread  
  18. {  
  19.     Q_OBJECT  
  20. public:  
  21. explicit Mythread(QObject *parent = 0);  
  22. signals:  
  23. public slots:  
  24. void mytimedout();  
  25. protected:  
  26. void run();  
  27.     QTimer _timer;  
  28. };  
  29. #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?
  1. void Mythread::mytimedout()  
  2. {  
  3.     cout << "thread id: " << QThread::currentThreadId() << endl;  
  4.     QEvent *eve = new Myevent((QEvent::Type)(mytype1));  
  5.     QCoreApplication::postEvent(QCoreApplication::instance(), eve);  
  6. }  
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?
  1. bool Mythread::event( QEvent * e )  
  2. {  
  3. switch(e->type())  
  4.     {  
  5. case mytype2:  
  6.         cout << "child thread:" << QThread::currentThreadId() << "recv a event, type is:" << e->type() << endl;  
  7. break;  
  8. default:  
  9. break;  
  10.     }  
  11. return QThread::event(e);  
  12. }  
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?
  1. bool Myapp::event( QEvent * e )  
  2. {  
  3. if(e->type() == (mytype1)){  
  4.         cout << "thread id:" << QThread::currentThreadId() << "recv a event, type is:" << e->type() << endl;  
  5.         QEvent *eve = new Myevent((QEvent::Type)mytype2);  
  6.         postEvent(_recv, eve);  
  7.     }  
  8. return QCoreApplication::event(e);  
  9. }  
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?
  1. void setReceive(QObject *obj){  
  2.         _recv = obj;  
  3.     }  
  4. protected:  
  5.     QObject *_recv;  
void setReceive(QObject *obj){
		_recv = obj;
	}
protected:
	QObject *_recv;

main函式 [cpp] view plaincopyprint?
  1. #include <QtCore/QCoreApplication>
  2. #include "mythread.h"
  3. #include "myapp.h"
  4. int main(int argc, char *argv[])  
  5. {  
  6.     Myapp a(argc, argv);  
  7.     cout << "reg value:" << QEvent::registerEventType(mytype1) << endl;  
  8.     cout << "reg value:" << QEvent::registerEventType(mytype2) << endl;  
  9.     cout << "main thread id:" << QThread::currentThreadId() << endl;  
  10.     Mythread thread;  
  11.     a.setReceive(&thread);  
  12. thread.start();  
  13. thread.moveToThread(&thread);  
  14. return a.exec();  
  15. }  
#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?
  1. thread.moveToThread(&thread// 這不是一個好的方法,這裡作為例子這麼用可以更快速讓例子執行起來
thread.moveToThread(&thread) // 這不是一個好的方法,這裡作為例子這麼用可以更快速讓例子執行起來

參考: