QT中的多线程-与主线程通信

今天回想研究生期间做的项目,用到了Qt的多线程通信,当时一点都不懂,就这照猫画虎地写,如今因为上次面试中问到了,觉得得好好准备下:

Qt 程序开始执行时,唯一的一个线程 —— 主线程 (main thread)也开始执行。主线程是唯一的,因为只有它才能创建 QApplication 或者是 QCoreApplication 对象,只有它才能通过应用程序对象调用 exec( ) 函数,只有它才能在 exec( ) 执行完毕后等待并处理事件。

主线程可以通过创建 QThread 子类对象开启一个新的线程,如果这些线程间需要相互通讯,它们可以使用共享变量,同时使用 mutexes,read-write locks,semaphores 或者 wait conditions 一些方法保持共享变量访问的同步性。但是由于这些技术可能锁定 event loop,同时还会冻结用户界面,所以其中没有一个能完成与主线程之间的通讯。

完成第二线程(secondary thread)与主线程之间的通讯的方法是:跨线程间的 signal-slot 连接。signal 一旦发出,其对应的 slot 函数便立即执行,这种连接是一种同步的机制。

但是当我们将不同线程中的对象连接在一起时,这种 signal-slot 通讯机制变得“不同步”(asynchronous)。signal-slot 机制的底层实现是传递一个 event,然后 slot 由 receiver 对象所在的线程中的 event loop 调用。默认情况下,一个 QObject 对象存在于创建它的线程中,但是任何时刻,调用 QObject : : moveToThread( ) 函数可以改变这种关系。


当时我们的程序中其实有两个大类,一个是Transaction类,一个是Transaction Thread类,可以看到Transaction是个抽象类,里面的纯虚函数Execute虽然在子类的实现中都是空的,但是目的就是为了让Transaction成为一个抽象类,

Transaction类:

class Transaction {
public:
	virtual ~Transaction() {
	}
	virtual void Execute() = 0;
	int getTransactID();
protected:
	int transactID;
};

class LoginTransaction: public Transaction {
public:
	LoginTransaction();
	void Execute();
private:
};

在cpp文件中对LoginTransaction的构造函数和必须实现的接口做定义(虽然是空的。。。)

int Transaction::getTransactID() {
	return transactID;
}

LoginTransaction::LoginTransaction() {
	transactID = TR_LOGIN_SUCCESS;
}

void LoginTransaction::Execute() {
}


然后在Transaction Tread类中,相当于第二个线程,通过single-slot向主线程发送信号。


TransactionTread.h文件


class TransactionThread: public QThread {
Q_OBJECT
public:
	TransactionThread();
	virtual ~TransactionThread();
	void addTransaction(Transaction *tr);
protected:
	void run();
signals:
	void loginSuccess();
private:
	QQueue<Transaction*> transactQueue;
	QWaitCondition transactAdded;
	QMutex mutex;
private:
	void ProcessTransact(Transaction* tr);
在这个类中,run 函数在自己的线程中执行,其它的函数则从主线程调用。在这个类中,维护着一个 transaction 队列,其中的每个 transaction 将一个接一个地被执行。
TransactionTread.cpp文件

Transaction * const EndTransaction = 0;

TransactionThread::TransactionThread() {
	start();
}

TransactionThread::~TransactionThread() {
	{
		QMutexLocker locker(&mutex);
		while (!transactQueue.isEmpty())
			delete transactQueue.dequeue();
		transactQueue.enqueue(EndTransaction);
		transactAdded.wakeOne();
	}
	wait();
}

void TransactionThread::addTransaction(Transaction *tr) {
	QMutexLocker locker(&mutex);
	transactQueue.enqueue(tr);
	transactAdded.wakeOne();
}

void TransactionThread::run() {
	Transaction *tr = 0;
	forever {
		{
			QMutexLocker locker(&mutex);
			if (transactQueue.isEmpty())
				transactAdded.wait(&mutex);
			tr = transactQueue.dequeue();
			if (tr == EndTransaction)
				break;
		}
		ProcessTransact(tr);
//		
//		if (tr->getTransactID() != TR_ADDED || )
//			delete tr;
	}
}

void TransactionThread::ProcessTransact(Transaction *tr) {
	switch (tr->getTransactID()) {
	case TR_LOGIN_SUCCESS:
		ProcessTrLogin(tr);
		break;
	case TR_LOGIN_FAIL:
		ProcessTrLoginFail(tr);
		break;
	case TR_ADDED:
		ForwardTransact(tr);
		//ProcessTrAdded(tr);
		break;
	case TR_ADDNORMAL:
		//ForwardTransact(tr);
		ProcessTrAddNormal(tr);
		break;
	case TR_STATUS:
		ProcessTrStatus(tr);
		break;
	case TR_VIEWCONTACT:
		ProcessTrViewContact(tr);
		break;
	case TR_NEW_CONTACT:
		ProcessTrNewContact(tr);
		break;
	case TR_DELETECONTACT:
		ForwardTransact(tr);
		break;
	case TR_BEDELETED:
		ForwardTransact(tr);
		break;
	case TR_NEW_IM:
		ProcessTrNewIM(tr);
		break;
	case TR_INS_CONTACT:
		ProcessTrInsContact(tr);
		break;
	case TR_PHONE_LOST:
		ProcessTrPhoneLost(tr);
		break;
	default:
		break;
	}
}

void TransactionThread::ProcessTrLoginFail(Transaction *tr) {
	tr->Execute();
	emit loginFailure();
}

void TransactionThread::ProcessTrLogin(Transaction *tr) {
	tr->Execute();
	emit loginSuccess();
}

调用 QThread : : start( ) 开启将要执行 transaction 的线程。在析构函数中,清空队列,将一个特殊的 EndTransaction 加入队列。唤醒线程,并使用 QThread : : wait( ) 等待线程结束。如果没有 wait( ),当其它的线程访问类中的成员变量时,程序有可能崩溃。 在析构函数中,QMutexLocker 的析构造函数将被调用。其中将 mutex 解锁,在 wait( ) 之前解锁这很重要,否则将引起死锁的可能性(第二线程一直等待 mutex 被解锁,而主线程一直等待第二线程完成而操持 mutex 不放)。

QWaitCondition : : wakeOne( ) 函数唤醒一个正在等待某个条件的线程。被唤醒的线程取决于操作系统的排程策略,并不能控制和提前预知哪个线程将被唤醒。如果需要唤醒某个指定的线程,通常需要使用不同的等待条件,使用不同的线程专门等待不同的等待条件。

addTransaction( ) 函数将一个 transaction 添加到队列中,并唤醒 transaction 线程。所有访问 transactions 的成员变量都由一个 mutex 保护,因为在第二线程遍历队列中的 transaction 时主线程可能修改这些变量。

run函数中定义了不同的执行方法,获取transaction的id,然后进行不同的侗族,也就是发射出不同的信号。

然后在主线程中,connect这些信号到主线程的槽函数中:

UIControl::UIControl(Manager *p, QApplication* a) {
	app = a;
	manager = p;
	loginpage = NULL;
	waitpage = NULL;
	main = NULL;
	msgbox = new MessageBox(this);
	tr_queue = new TransactionThread();
	connect(tr_queue, SIGNAL(loginSuccess()), this, SLOT(ShowMainFrame()));
没写完,UIControl就是个主界面,通过tr_queue中的不同信号,来调用不同的槽函数。

上一篇:Spring+SpringMVC+Hibernate简单整合(转)


下一篇:为什么applicationContext.xml和spring-servlet.xml中都有注解过滤

相关文章