Qt4.7中,線程,信號,事件的一點理解

首先,寫個線程類,繼承自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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章