You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复基于WINAPI的用户选择超时功能中的无限循环问题?

问题修复方案

核心问题分析

  • 无限循环原因:主线程在用户输入一次后进入do-while循环,但循环内没有重新等待用户输入新选项,option值保持不变,只要不是3就会一直重复执行循环。
  • 超时逻辑失效:e.wait(5000)仅执行一次,后续循环中没有重新监听用户输入或超时事件,输入线程执行完一次后就结束,无法处理后续的超时场景。
  • 线程终止不安全:使用TerminateThread强制终止线程可能导致资源泄漏(比如std::cin状态异常),应该用更优雅的方式让线程主动退出。

修复步骤

  • 重构输入逻辑,每次循环都重新创建输入线程并等待输入/超时事件。
  • 新增退出事件控制线程主动退出,替代不安全的TerminateThread。
  • 在循环内处理用户输入后的逻辑,同时重置事件重新启动输入监听。

修改后的完整代码

#include <windows.h>
#include <iostream>

namespace jsw {
    namespace threading {
        class auto_event {
        public:
            auto_event() : _event(CreateEvent(nullptr, false, false, nullptr)) {}
            ~auto_event() { CloseHandle(_event); } // 补充析构释放资源

            BOOL wait(DWORD timeout = INFINITE) const
            {
                return WaitForSingleObject(_event, timeout) == WAIT_OBJECT_0;
            }

            BOOL set() { return SetEvent(_event); }
            BOOL reset_event() { return ResetEvent(_event); }
        private:
            HANDLE _event;
        };

        class thread {
        public:
            static thread start(
                LPTHREAD_START_ROUTINE fn, LPVOID args = nullptr,
                DWORD state = 0, DWORD timeout = 5000)
            {
                HANDLE hThread = CreateThread(nullptr, 0, fn, args, state, nullptr);
                return thread(hThread, timeout);
            }

            static void sleep(DWORD milliseconds) { Sleep(milliseconds); }
            static void exit(DWORD exitCode) { ExitThread(exitCode); }
        public:
            thread(HANDLE thread, DWORD timeout) : _thread(thread), _timeout(timeout) {}
            ~thread() { if (_thread != INVALID_HANDLE_VALUE) CloseHandle(_thread); }

            DWORD exit_code() const
            {
                DWORD exitCode = NO_ERROR;
                GetExitCodeThread(_thread, &exitCode);
                return exitCode;
            }

            HANDLE handle() const { return _thread; }
            BOOL is_alive() const { return exit_code() == STILL_ACTIVE; }
            DWORD join() { return WaitForSingleObject(_thread, _timeout); }
        private:
            HANDLE _thread = INVALID_HANDLE_VALUE;
            DWORD _timeout;
        };
    }
}

// 结构化线程参数,包含选项、输入完成事件、退出事件
struct ThreadArgs {
    int* option;
    jsw::threading::auto_event* inputDoneEvent;
    jsw::threading::auto_event* exitEvent;
};

DWORD WINAPI get_option(LPVOID args)
{
    ThreadArgs* params = static_cast<ThreadArgs*>(args);
    
    // 同时监听退出事件和输入完成事件,优先响应退出信号
    HANDLE events[] = { params->exitEvent->handle(), params->inputDoneEvent->handle() };
    DWORD waitResult = WaitForMultipleObjects(2, events, FALSE, INFINITE);
    
    if (waitResult == WAIT_OBJECT_0) {
        // 收到退出信号,线程主动退出
        return NO_ERROR;
    }

    // 等待用户输入
    std::cin >> *params->option;
    params->inputDoneEvent->set();

    return NO_ERROR;
}

int main()
{
    using namespace jsw::threading;

    int option{};
    auto_event inputDoneEvent;
    auto_event exitEvent;
    ThreadArgs args{ &option, &inputDoneEvent, &exitEvent };

    while (true) {
        inputDoneEvent.reset_event();
        thread worker = thread::start(get_option, &args);

        // 等待用户输入或超时
        if (inputDoneEvent.wait(5000)) {
            system("cls");
            switch (option) {
            case 1: 
                std::cout << "执行选项1的操作..." << std::endl;
                break;
            case 2: 
                std::cout << "执行选项2的操作..." << std::endl;
                break;
            case 3: 
                // 发送退出信号给线程,等待线程正常结束
                exitEvent.set();
                worker.join();
                std::cout << "***Program Exit***" << std::endl;
                return 0;
            default: 
                std::cout << "Option not valid! 请重新输入..." << std::endl;
                break;
            }
            // 短暂延迟让用户查看提示信息
            sleep(2000);
        }
        else {
            // 超时触发,发送退出信号并终止程序
            exitEvent.set();
            worker.join();
            std::cout << "***PROGRAM END***" << std::endl;
            return 0;
        }
    }
}

关键修改说明

  • 资源泄漏修复:给auto_event补充析构函数,确保HANDLE资源被正确释放。
  • 线程安全终止:新增exitEvent,线程通过监听该事件主动退出,避免强制终止带来的风险。
  • 循环逻辑重构:每次循环都重置事件并启动新的输入线程,保证每次都能响应输入或超时。
  • 用户体验优化:无效选项后添加短暂延迟,让用户能看到提示信息再进入下一轮输入。

内容的提问来源于stack exchange,提问作者RexyCode

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 10:20:39