我可以在 MFC 中有多个 GUI 线程吗?

2022-01-12 00:00:00 multithreading user-interface c++ mfc

我有一个基于 MFC 的大型应用程序,它在主线程中包含一些可能非常慢的任务.这可能会使应用程序在实际执行一项长期任务时显得挂起.从可用性的角度来看,我想为用户提供更多关于进度的反馈,并可以选择以干净的方式中止任务.虽然将长任务分成单独的线程将是一个更好的长期解决方案,但我认为实用的短期解决方案是创建一个封装在其自己的对象中的新 GUI 线程,并带有对话框,包括进度条和取消按钮,用于类似于 CWait 对象的方式.主线程通过 IsCancelled 方法监控取消状态,并在需要时通过 throw 结束.

I have a large MFC based application that includes some potentially very slow tasks in the main thread. This can give the appearance that the application has hung when it is actually working its way through a long task. From a usability point of view, I'd like to be giving the user some more feedback on progress, and have an option to abort the task in a clean manner. While hiving the long tasks off into separate threads would be a better long term solution, I'm thinking a pragmatic short term solution is create a new GUI thread encapsulated in its own object complete with dialog including progress bar and cancel button, used in a similar manner to a CWait object. The main thread monitors the cancel status via an IsCancelled method, and finishes via a throw when required.

这是一种合理的方法吗?如果是的话,是否已经有一些 MFC 代码可供我使用,或者我应该自己开发吗?第一张草图是这样的

Is this a reasonable approach, and if so is there some MFC code out there already that I can use, or should I roll my own? First sketch looks like this

class CProgressThread : public CWinThread
{
public:
    CProgressThread(int ProgressMax);      
    ~CProgressThread()
    void SetProgress(int Progress);
    BOOL IsCancelled();
private:
   CProgressDialog  *theDialog;
}

void MySlowTask()
{
   CProgressThread PT(MaxProgress);
   try
   {
       {
           {  // deep in the depths of my slow task
              PT.SetProgress(Progress);
              if (PT.IsCancelled())
                 throw new CUserHasHadEnough; 
           }
        }
    }
    catch (CUserHasHadEnough *pUserHasHadEnough)
    {
        // Clean-up
    }
}    

通常,我倾向于拥有一个 GUI 线程和许多工作线程,但这种方法可能会为我节省大量重构和测试.任何严重的潜在陷阱?

As a rule, I tend to have one GUI thread and many worker threads, but this approach could possibly save me a bunch of refactoring and testing. Any serious potential pitfalls?

推荐答案

简答,是的,你可以在 MFC 中拥有多个 GUI 线程.但是除了创建的线程之外,您不能直接访问 GUI 组件.原因是 MFC 下的 Win32 存储基于每个线程的 GUI 处理程序.这意味着一个线程中的处理程序对另一个线程不可见.如果您跳转到 CWinThread 类源代码,您可以在那里找到一个处理程序映射属性.

Short answer, Yes, you can have multiple GUI thread in MFC. But you can't access the GUI component directly other than the created thread. The reason is because the Win32 under the MFC stores the GUI handler per thread based. It means the handler in one thread isn't visible to another thread. If you jump to the CWinThread class source code, you can find a handler map attribute there.

Windows (MFC) 在工作线程和工作线程之间没有硬性区别.图形用户界面线程.任何线程都可以在创建消息队列后转换为 GUI 线程,消息队列是在第一次调用与消息相关的函数后创建的,例如 GetMessage().

Windows (MFC) doesn't has hard difference between the worker thread & GUI thread. Any thread can be changed to GUI thread once they create the message queue, which is created after the first call related to the message, such as GetMessage().

在您上面的代码中,如果进度条是在一个线程中创建的,而 MySlowWork() 是在另一个线程中调用的.您只能使用 CProgressThread 属性而不触及 Win32 GUI 相关函数,例如 close、setText、SetProgress...,因为它们都需要 GUI 处理程序.如果您确实调用了这些函数,则错误将是找不到指定的窗口,因为该处理程序不在线程处理程序映射中.

In your above code, if the progress bar is created in one thread and MySlowWork() is called in another thread. You can only use the CProgressThread attributes without touch the Win32 GUI related functions, such as close, setText, SetProgress... since they all need the GUI handler. If you do call those function, the error will be can't find the specified window since that handler isn't in the thread handler mapping.

如果您确实需要更改 GUI,则需要将消息发送到该进度条所有者线程.让该线程通过 PostThreadMessage 自己处理消息(消息处理程序),详情参考MSDN.

If you do need change the GUI, you need send the message to that progress bar owner thread. Let that thread handles the message by itself (message handler) through the PostThreadMessage, refer to MSDN for detail.

相关文章