我可以在MFC中使用多个GUI线程吗?
我可以在MFC中使用多个GUI线程吗?
我有一个基于MFC的大型应用程序,在主线程中包含一些可能非常慢的任务。当应用程序在处理长时间任务时,这可能会给人一种应用程序已经挂起的假象。从可用性的角度来看,我希望能给用户一些更多的进展反馈,并且有一个以清洁的方式中止任务的选项。虽然将长时间任务分离到单独的线程中可能是一个更好的长期解决方案,但我认为在短期内采取一种实用的方法是创建一个封装在自己对象中的新的GUI线程,包括进度对话框和取消按钮,类似于CWait对象的使用方式。主线程通过IsCancelled方法监视取消状态,并在需要时通过throw语句结束。
这是一个合理的方法吗?如果是的话,是否已经有一些现成的MFC代码可以使用,还是我应该自己编写?初步草图如下:
class CProgressThread : public CWinThread { public: CProgressThread(int ProgressMax); ~CProgressThread(); void SetProgress(int Progress); BOOL IsCancelled(); private: CProgressDialog *theDialog; }; void MySlowTask() { CProgressThread PT(MaxProgress); try { { { // 我的慢任务的深处 PT.SetProgress(Progress); if (PT.IsCancelled()) throw new CUserHasHadEnough; } } } catch (CUserHasHadEnough *pUserHasHadEnough) { // 清理工作 } }
通常情况下,我倾向于有一个GUI线程和多个工作线程,但这种方法可能能够节省我大量的重构和测试工作。有什么严重的潜在问题吗?
可以在MFC中拥有多个GUI线程,但是不能直接访问GUI组件,除非是创建该线程的线程。这是因为MFC下的Win32根据线程存储GUI处理程序。这意味着一个线程中的处理程序对于另一个线程是不可见的。如果查看CWinThread类的源代码,可以找到一个处理程序映射属性。
Windows(MFC)在工作线程和GUI线程之间没有明显的区别。任何线程一旦创建了消息队列,就可以变成GUI线程,消息队列是在与消息相关的第一次调用后创建的,比如GetMessage()。
在上面的代码中,如果进度条是在一个线程中创建的,而MySlowWork()在另一个线程中调用。你只能使用CProgressThread的属性,而不能触及Win32 GUI相关的函数,比如close、setText、SetProgress...,因为它们都需要GUI处理程序。如果调用这些函数,会出现找不到指定窗口的错误,因为该处理程序不在线程处理程序映射中。
如果确实需要更改GUI,需要向该进度条的所有者线程发送消息。让该线程自己处理消息(消息处理程序),通过PostThreadMessage发送消息,详细信息请参阅MSDN。