Legend Since 1984
Cruising between Fantasy and Reality...

Thursday, May 10, 2007

Thread and Class in MFC

毕业设计的程序LipTrackingSystem是用MFC写的。原先的版本把所有的工作都放到主线程中去完成,这样的副作用是明显的——一旦程序进入处理函数,其流程必须等到处理函数结束之后才能返回。也就是说,在处理视频流数据的时候,窗口是无法相应系统或用户的消息的。最明显的表现是点击窗口没有反应,窗口出现“假死”状态,只有当处理函数返回时窗口才重新“活”过来。通常,当处理函数的执行时间非常长的时候,程序设计者希望给用户提供暂停处理的功能。但是,单线程的情况下,由于窗口假死,暂停的功能无从谈起。当然有一种方法是给窗口加入若干状态,并在处理函数中定时地主动探知有无新的消息。但是,这样的程序一来写起来麻烦,二来可读性较差,非我等追求优雅编程的人士所能接受的。那么,另外一条光明的出路就是——多线程!

将窗口的相应函数作为主线程,同时,新开辟一个线程来运行处理函数(工作线程)。由于OS可以自动地分配线程的时间片,这样,主线程和工作线程就可以同时工作,而不需要人为地在处理函数中主动监听消息了。甚之,暂停/回复的功能也很简单,直接对工作线程执行相应的操作就可以了。

不过,MFC中实现多线程间的合作却没有预想中那么一帆风顺。Win32的API规定,线程的处理函数必须有固定的格式,即void fp(void *)。在MFC中,窗口是用CView的派生类来表示的。主线程和工作线程要配合工作,自然希望工作线程能访问主线程——即窗口内的变量。自然而然地想到把一个CView派生类的成员函数作为线程处理函数。不过,在C++中所以类的成员函数都回自动加上一个this指针作为参数,这样,虽然表面上成员函数可以定义成void fp(void *)的形式,但实际上它还有一个隐含的this指针作为第二个参数,因此不满足线程处理函数的格式,编译不能成功。接着想到可以使用static修饰符,这样就可以避免隐含的this指针。不过新的问题是,static成员函数是独立于对象的,不能访问非static的类成员变量。还好创建线程的时候可以为处理函数添加一个指针作为参数,直接把窗口的this指针传递过去即可。

现在可以编译了,不过问题似乎不愿意主动终结。在处理函数中可以通过this指针调用窗口类的函数、访问成员变量,包括private的,但是有一个地方去出现了错误——当窗口调用自己的外框MainFrame修改StatusBar的时候,却总是出现运行时错误。用step-in逐步的跟踪后发现在验证StatusBar对象是否有效的地方出现了异常。奇怪呀,原先都是正常的,为什么一放到工作线程中就异常了呢?这个问题最后还是不得其解,不过我已经没有时间理会这个小破问题了。既然直接调用MainFrame的成员不行,那就祭起Win32下的终极法宝——SendMessage吧!自己定义一个消息的类型,在MainFrame的MessageMap中设置函数捕获这个消息。遇到要设置StatusBar的时候,发送消息同时传递参数,MainFrame在自己的地盘上捕获消息、设置StatusBar的值,再有异常就天理不容了。

终于,MFC下类和多线程的问题被我解决了。心情不错,哈哈。

Labels:

0 Comments:

Post a Comment

<< Home