MFC执行线程问题

时间:2018-07-13 13:56:05

标签: c++ multithreading mfc

我有一个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));
    // …
}

2 个答案:

答案 0 :(得分:0)

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

我的解决方法是将控件放回窗口以使其使用消息:

// … 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;
     }
}

答案 1 :(得分:0)

正确的方法是使用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");
}

如果无法中断该功能,则必须启动第二个线程。

相关问题