MFC执行线程问题

Posted

技术标签:

【中文标题】MFC执行线程问题【英文标题】:MFC executing thread problems 【发布时间】:2018-07-13 13:56:05 【问题描述】:

我有一个 CDialog,它允许用户在硬盘中导航、列出和显示文件预览。在某些情况下,可能会有很多繁重的文件,这些情况需要很长时间,因此我们将加载操作移到了单独的线程中。

现在,我希望在单独的线程中移动磁盘访问可以让我正常使用 CDialog,但这不会发生,因此我无法滚动或移动窗口。

我是否在此过程中遗漏了什么?代码如下:

void CMyDialog::LoadFiles()

    // …
    std::thread load_file(LoadingRoutine, reinterpret_cast<void *>(&data));
    load_file.detach();
    // same happens if I use Afx functions
    // AfxBeginThread(&CMyDialog::LoadingRoutine, reinterpret_cast<void *>(&data));
    // …

【问题讨论】:

这还不足以确定问题所在。当您闯入调试器时,代码挂在哪里?你能提供一个minimal reproducible example吗? 代码没有挂起:在线程完成工作之前我无法使用对话框。 似乎“数据”是一种共享资源,并在对话框的其他地方使用。使用 std::promise 和 std::future 获取线程的状态。如果未来还没有准备好,请向对话框发出信号以打印其他消息并继续。 我错过了什么是的!正如 IInspectable 所说,我们需要一个 minimal reproducible example - 我们可以自己检查、编译、运行和测试的东西。 【参考方案1】:

问题部分修复:似乎使用线程会阻​​止窗口定期使用消息,即使它是非阻塞调用。

我的解决方法是把控制权交还给窗口,让它消费消息:

// … thread stuff
for (auto nI = 0; nI < nCount; nI++)

     // Heavy computing
     nSleepStep = 5;
     nSleepTime = 200;
     while (nSleepTime > nSleepStep)
     
          while(PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE))
          
             switch( msg.message ) 
              
             case WM_TIMER : 
             case WM_PAINT :
               TranslateMessage( &msg );
               DispatchMessage( &msg );
               break;
             
         
         std::this_thread::sleep_for(std::chrono::milliseconds(nSleepStep));
         nSleepTime -= nSleepStep;
     

【讨论】:

【参考方案2】:

正确的方法是使用AfxBeginThreadstd::thread::detach。您将能够在主 UI 线程中正常使用对话框。

或者,您可以在单个线程中执行此操作,假设您的函数可以被中断并分解为不同的部分。例如,假设您有一个需要 3 秒才能完成的功能:

Sleep(3000);

它可以分解成30个部分并模拟为

for (int i = 0; i < 30; i++) 
    Sleep(100);

您可以在每次移动后更新绘画。请注意,必须忽略其他对话框消息,因为这是一个线程,您一次只能做一件事。您应该禁用控件以让用户知道对话框正忙。示例:

void update_paint()

    MSG msg;
    while(PeekMessage(&msg, m_hWnd, 0, 0, PM_REMOVE))
    
        if(msg.message == WM_COMMAND && msg.wParam == IDCANCEL)  //cancel reuested
        if(msg.message == WM_PAINT ||
            (msg.message >= WM_NCCALCSIZE && msg.message <= WM_NCACTIVATE) ||
            (msg.message >= WM_NCMOUSEMOVE && msg.message <= WM_NCMBUTTONDBLCLK))
        
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        
    


void CMyDialog::single_thread()

    MessageBox(L"start");
    //disable child controls to let user know the dialog is busy
    for(CWnd *p = GetWindow(GW_CHILD); p; p = p->GetWindow(GW_HWNDNEXT))
        p->EnableWindow(FALSE);

    for(int i = 0; i < 30; i++)
    
        Sleep(100);
        update_paint();
    

    //enable child controls
    for(CWnd *p = GetWindow(GW_CHILD); p; p = p->GetWindow(GW_HWNDNEXT))
        p->EnableWindow(TRUE);
    MessageBox(L"done");

如果函数不能被中断,则需要启动第二个线程。

【讨论】:

以上是关于MFC执行线程问题的主要内容,如果未能解决你的问题,请参考以下文章

如何为 MFC 线程设置超时

MFC多线程安全问题。

mfc 线程问题 会报内存不可读来帮下忙吧

c++/mfc 子线程结束后再来执行主线程下操作 该怎么处理

MFC中如何让多线程按先后顺序执行,第一个来的先执行,以后按先后到达的顺序执行

是否有可能/有必要在单独的线程中调用 OnDraw [MFC]