【问题标题】:separate thread to prevent progress bar from 'freezing' in c++单独的线程以防止进度条在 C++ 中“冻结”
【发布时间】:2021-09-05 06:57:50
【问题描述】:

我建立了一个最简单的进度条,它显示了我的 png 帧生成的进度。这是过程

BOOL myProc(HWND hwndParent, HINSTANCE hInst) {
    RECT rcClient;
    int cyVScroll;
    HWND hwndPB;
    DWORD cb;

    GetClientRect(hwndParent, &rcClient);
    cyVScroll = GetSystemMetrics(SM_CYVSCROLL);

    hwndPB = CreateWindowEx(0, PROGRESS_CLASS, (LPTSTR) NULL, 
                            WS_CHILD | WS_VISIBLE, rcClient.left, 
                            rcClient.bottom - cyVScroll, 
                            rcClient.right, cyVScroll, 
                            hwndParent, (HMENU) 0, hInst, NULL);
    int N = lastFrame;
    SendMessage(hwndPB, PBM_SETRANGE, 0, MAKELPARAM(0, N));
    SendMessage(hwndPB, PBM_SETSTEP, (WPARAM)1, 0);


    
    for (int i = 0; i < N + 1; i++) {
        int frame = create_pngInt(i);
        SendMessage(hwndPB, PBM_STEPIT, 0, 0);
        }

    DestroyWindow(hwndPB);
    DestroyWindow(hwndParent);
    return TRUE;

}

我希望我的窗口只承载这个进度条。当myProc中的循环完成后,宿主窗口应该被销毁。

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE PrevInst, LPSTR args, int ncmdshow) {
    WNDCLASSW wc = { 0 };
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hInstance = hInst;
    wc.lpszClassName = L"GenPng";
    wc.lpfnWndProc = WindowProcedure;
    wc.hbrBackground = CreateSolidBrush(0x00171c00);
    wc.hIcon = hIcon;

    if (!RegisterClassW(&wc))
        return -1;
    
    hMainWindow = CreateWindowW(L"GenPng", L"Generating pngs", WS_VISIBLE | WS_OVERLAPPEDWINDOW,
        200,
        100, 300, 58, NULL, NULL, NULL, NULL);
    
    std::thread q(myProc, hMainWindow, hInst);
    MSG msg = { 0 };
    
    while (GetMessage(&msg, NULL, NULL, NULL))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

但是,窗口hMainWindow 没有被破坏(!)并且进程myProc 不会自行终止(在myProc 中的循环完成之后)。 好像没看懂什么重要的东西。

我查过了。如果我不创建单独的线程而只是在 WinMain 中运行 myProc,则托管进度条的窗口在 myProc 返回后被破坏。而这正是我想要的。但是进度条会在几秒钟后冻结(。这就是为什么我需要一个单独的线程。

根据您的评论,我修改了如下代码:

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE PrevInst, LPSTR args, int ncmdshow) {
    RECT rect;
    WNDCLASSW wc = { 0 };
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hInstance = hInst;
    wc.lpszClassName = L"GenPng";
    wc.lpfnWndProc = WindowProcedure;
    wc.hbrBackground = CreateSolidBrush(0x00171c00);
    wc.hIcon = hIcon;

    if (!RegisterClassW(&wc))
        return -1;

    hMainWindow = CreateWindowW(L"GenPng", L"Generating pngs", WS_VISIBLE | WS_OVERLAPPEDWINDOW,
        200,
        100, 300, 58, NULL, NULL, NULL, NULL);
    SetWindowPos(hMainWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

    
    RECT rcClient;
    int cyVScroll;
    HWND hwndPB;
    DWORD cb;

    GetClientRect(hMainWindow, &rcClient);
    cyVScroll = GetSystemMetrics(SM_CYVSCROLL);

    hwndPB = CreateWindowEx(0, PROGRESS_CLASS, (LPTSTR)NULL,
        WS_CHILD | WS_VISIBLE, rcClient.left,
        rcClient.bottom - cyVScroll,
        rcClient.right, cyVScroll,
        hMainWindow, (HMENU)0, hInst, NULL);
    int N = lastFrame;
    SendMessage(hwndPB, PBM_SETRANGE, 0, MAKELPARAM(0, N));
    SendMessage(hwndPB, PBM_SETSTEP, (WPARAM)1, 0);
    
    std::thread q(myProc, hMainWindow, hInst, hwndPB);

    MSG msg = { 0 };
    while (GetMessage(&msg, hMainWindow, NULL, NULL))
    {
        if (msg.message == WM_QUIT) {
            DestroyWindow(hMainWindow);
        }
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

但是,我知道我没有正确处理 WM_QUIT 事件,整个事情并没有被破坏。能否请您再详细说明一下,如果其余代码都可以吗?

【问题讨论】:

  • 你需要在工作线程中调用create_pngInt(将hwndPB作为参数传递给这个线程)
  • 您最好将所有 UI(包括进度条)留在主线程中并在后台线程中执行 CPU 密集型工作。

标签: c++ multithreading winapi


【解决方案1】:

调用CreateWindowEx的线程必须是执行GetMessage循环的线程。

它不会被销毁,因为没有任何东西为它处理事件和消息,包括WM_DESTROY 消息。

将您的窗口创建保留在您的主线程中(并且仅限该线程!),并仅使用 PostMessage / SendMessage 发送更新事件,最后向主线程拥有的窗口发送销毁消息。

最终使用PostQuitMessageWM_DESTROY 处理到您的主窗口,您就完成了。

全文:

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE PrevInst, LPSTR args, int ncmdshow) {
    RECT rect;
    WNDCLASSW wc = { 0 };
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hInstance = hInst;
    wc.lpszClassName = L"GenPng";
    wc.lpfnWndProc = WindowProcedure;
    wc.hbrBackground = CreateSolidBrush(0x00171c00);
    wc.hIcon = hIcon;

    if (!RegisterClassW(&wc))
        return -1;

    hMainWindow = CreateWindowW(
        L"GenPng", L"Generating pngs", WS_VISIBLE | WS_OVERLAPPEDWINDOW,
        200,
        100, 300, 58, NULL, NULL, NULL, NULL
    );
    SetWindowPos(hMainWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);


    RECT rcClient;
    int cyVScroll;
    HWND hwndPB;
    DWORD cb;

    GetClientRect(hMainWindow, &rcClient);
    cyVScroll = GetSystemMetrics(SM_CYVSCROLL);

    hwndPB = CreateWindowEx(
        0, PROGRESS_CLASS, (LPTSTR)NULL,
        WS_CHILD | WS_VISIBLE, rcClient.left,
        rcClient.bottom - cyVScroll,
        rcClient.right, cyVScroll,
        hMainWindow, (HMENU)0, hInst, NULL
    );
    int N = lastFrame;
    SendMessage(hwndPB, PBM_SETRANGE, 0, MAKELPARAM(0, N));
    SendMessage(hwndPB, PBM_SETSTEP, (WPARAM)1, 0);

    std::thread q([hMainWindow, hInst, hwndPB, N]() {
        for (int i = 0; i < N + 1; i++) {
            SendMessage(hwndPB, PBM_STEPIT, 0, 0);
        }
        DestroyWindow(hMainWindow);
    });

    MSG msg = { 0 };
    while (GetMessage(&msg, NULL, NULL, NULL))
    {
        if (msg.message == WM_DESTROY) {
            PostQuitMessage(0); 
        }
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    q.join();
    
    // Exit code from PostQuitMessage() is still in msg.param
    return msg.param;
}

【讨论】:

  • 谢谢,Ext3h!我马上试一试!
  • 亲爱的 Ext3h 和 RbMm,我刚刚编辑了我的主要帖子。我想,我更接近了,但仍然没有成功。你能再快速浏览一下吗?
  • @YaroslavRodionov 你打电话给PostQuitMessage 来触发WM_QUIT 了吗?如果没有,请这样做。而且您也不能“处理”WM_QUIT,如果发送正确,它会导致GetMessage 返回 0 并因此退出该循环。在循环之后,您还忘记了最后的 q.join()
  • 亲爱的 Ext3h,感谢您的大力帮助。奇怪的是,这个窗口仍然存在。关于 WM_QUIT。在 hMainWindow 的 CallBack 函数中,我添加了 case WM_DESTROY: PostQuitMessage(0); break;
猜你喜欢
  • 2015-11-04
  • 1970-01-01
  • 2012-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-02
相关资源
最近更新 更多