【问题标题】:Button to join thread causes win32/winAPI application to freeze加入线程的按钮导致 win32/winAPI 应用程序冻结
【发布时间】:2021-05-10 18:12:38
【问题描述】:

标题几乎概括了我的问题。我有下面显示的这个功能,当我按下按钮加入线程时,应用程序停止响应。我对 C++ 中的多线程相当陌生,我真的不知道我在做什么或为什么会发生这种情况。

HWND hwnd;
//this stops the declaration of the thread from saying that hwnd doesn't exist
bool stopRaidAction;

void JoinLoop(HWND hwnd)
{
    for (std::string tokenString; std::getline(std::cin,tFileContents,'\n');) {
        if(stopRaidAction==false)
        {
            if(tokenString.length()==0){break;}
            if(tokenString.length()!=0){
                //send post request to join the server using token through proxy
            }
        }
        if(stopRaidAction){break;}
    }
}

std::thread joinThread(JoinLoop, hwnd);

问题在于“JOIN_RAID”

LRESULT CALLBACK WindowProcessMessages(HWND hwnd, UINT msg, WPARAM param, LPARAM lparam) {
    switch (msg) {
    case WM_CREATE:
        Controls(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    case WM_COMMAND:
    {
        if ((HIWORD(param) == BN_CLICKED) && (lparam != 0))
        {
            switch (LOWORD(param))
            {
                case JOIN_RAID:
                    stopRaidAction=false;
                    MessageBeep(MB_OK);
                    joinThread.join();
                    break;

                case STOP_RAID:
                    stopRaidAction=true;
                    MessageBeep(MB_OK);
                    break;
            }
        }
        break;
    }
    default:
        return DefWindowProc(hwnd, msg, param, lparam);
    }
    return 0;
}

【问题讨论】:

  • stopRaidAction 不是atomic,所以你有一个数据竞争(一个线程读取和另一个没有同步的写入),这是未定义的行为。挂起是未定义行为的有效表现。
  • @Supergamer5465:看起来你加入线程的情况下,你永远不会触发线程函数中循环的终止条件。
  • 我把它变成了原子的,但它仍然挂起。我怎么能触发这个“终止条件”?

标签: c++ multithreading winapi


【解决方案1】:

joinThread.join() 阻塞调用线程,直到JoinLoop() 退出。如果您在主 UI 线程中执行该调用,您将阻止您的消息循环能够处理新消息。这就是你的 UI 冻结的原因。

JoinLoop() 正在等待 2 个条件 - 正在输入用户输入,stopRaidAction 在处理该输入时为真。即使用户输入输入,您的主线程在调用join() 之前将stopRaidAction 设置为false,因此除非JoinLoop() 本身或第三个线程将stopRaidAction 设置为true,然后JoinLoop() 获胜'不退出,所以join() 不会解除阻止。

即使stopRaidAction 能够在join() 等待时设置为trueJoinLoop() 仍然要等到下一次用户输入时才会退出,所以它可以看到新的stopRaidAction 值。无法解锁待处理的std::getline() 呼叫。所以你应该重写你处理控制台输入的方式,这样你就可以及时停止等待输入。例如,请参阅Win32 - read from stdin with timeout

std::thread::join() 不是你应该调用的东西,直到你准备好终止工作线程。这段代码没有做什么。

鉴于显示的代码,在单击JOIN_RAID 之前根本不创建线程可能更有意义,然后在单击“STOP_RAID”时终止线程,例如:

bool stopRaidAction = false;
std::thread joinThread;

void JoinLoop(HWND hwnd)
{
    std::string tokenString;
    while (!stopRaidAction) {
        // read input with timeout...
        if(tokenString.empty()){ break; }
        //send post request to join the server using token through proxy
    }
}

LRESULT CALLBACK WindowProcessMessages(HWND hwnd, UINT msg, WPARAM param, LPARAM lparam) {
    switch (msg) {
    case WM_CREATE:
        Controls(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    case WM_COMMAND:
    {
        if ((HIWORD(param) == BN_CLICKED) && (lparam != 0))
        {
            switch (LOWORD(param))
            {
                case JOIN_RAID:
                    stopRaidAction=false;
                    MessageBeep(MB_OK);
                    joinThread = std::thread(JoinLoop, hwnd);
                    break;

                case STOP_RAID:
                    stopRaidAction=true;
                    MessageBeep(MB_OK);
                    if (joinThread.joinable()) joinThread.join();
                    break;
            }
        }
        break;
    }
    default:
        return DefWindowProc(hwnd, msg, param, lparam);
    }
    return 0;
}

但这只是猜测,因为您一开始并没有解释您实际想要实现的目标。

【讨论】:

    【解决方案2】:

    更新:

    这是 FOR 循环。我真的快死了。我什至不知道我花了多少时间调试才发现 for 循环被搞砸并导致挂起。

    【讨论】:

    • 请发布修复代码并解释旧代码有什么问题以及修复如何解决它。这是一个协作站点,下一个遇到此问题的人希望看到解决方案。
    猜你喜欢
    • 2021-11-22
    • 1970-01-01
    • 1970-01-01
    • 2015-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-02
    • 1970-01-01
    相关资源
    最近更新 更多