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

如何优化WINAPI多线程程序并替换C++标准I/O流?

WINAPI多线程输入超时程序优化方案

原程序存在的问题

  • 线程终止不安全:使用TerminateThread强制终止线程,会导致线程内未释放的资源(如标准IO内部锁、缓冲区)泄漏,引发程序不稳定。
  • 线程资源浪费:每次循环都创建新线程,线程的创建与销毁会带来不必要的系统开销。
  • 资源泄漏:auto_event类未在析构中调用CloseHandle关闭事件句柄,导致内核对象泄漏。
  • C++标准IO的问题:标准IO流与WINAPI控制台机制兼容性差,多线程环境下易出现同步问题,且std::cin的阻塞无法优雅响应终止信号。
  • 参数传递不安全:用LPVOID数组强制类型转换传递参数,易出现类型错误,代码可读性差。
  • 逻辑漏洞:输入选项3时未退出循环,程序会继续创建新线程。

优化方案说明

1. 替换C++标准IO为WINAPI控制台API

使用WriteConsole、ReadConsole等原生WINAPI控制台函数,避免标准库的兼容性和同步问题,更贴合WINAPI生态。

2. 线程复用与安全终止

  • 仅创建一个输入线程,通过事件控制线程的唤醒和退出,避免重复创建销毁线程的开销。
  • 使用终止事件通知线程自行退出,替代TerminateThread,保证线程能正确清理资源后退出。

3. 修复资源泄漏

给auto_event类添加析构函数,调用CloseHandle关闭事件句柄,释放内核对象资源。

4. 类型安全的参数传递

定义结构体封装线程所需参数,避免强制类型转换带来的风险,提升代码可读性。

5. 修正循环退出逻辑

当用户输入选项3时,触发终止事件,等待线程退出后结束主程序循环。

优化后的完整代码

#include <windows.h>

namespace jsw {
    namespace threading {
        class auto_event {
        public:
            auto_event() : _event(CreateEvent(nullptr, FALSE, FALSE, nullptr)) {}
            ~auto_event() { 
                if (_event != nullptr) {
                    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); }

            // 禁止拷贝和移动,避免句柄重复释放
            auto_event(const auto_event&) = delete;
            auto_event& operator=(const auto_event&) = delete;
            auto_event(auto_event&&) = delete;
            auto_event& operator=(auto_event&&) = delete;

        private:
            HANDLE _event;
        };

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

            static void sleep(DWORD milliseconds) { Sleep(milliseconds); }
            static void exit(DWORD exitCode) { ExitThread(exitCode); }

            thread(HANDLE thread) : _thread(thread) {}
            ~thread() { 
                if (_thread != nullptr) {
                    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(DWORD timeout = INFINITE) { return WaitForSingleObject(_thread, timeout); }

            // 禁止拷贝和移动
            thread(const thread&) = delete;
            thread& operator=(const thread&) = delete;
            thread(thread&&) = delete;
            thread& operator=(thread&&) = delete;

        private:
            HANDLE _thread;
        };
    }
}

// 线程参数结构体,类型安全
struct ThreadArgs {
    int* option;
    jsw::threading::auto_event* inputEvent;
    jsw::threading::auto_event* terminateEvent;
};

DWORD WINAPI get_option(LPVOID args)
{
    ThreadArgs* threadArgs = static_cast<ThreadArgs*>(args);
    HANDLE events[] = { threadArgs->terminateEvent->handle(), threadArgs->inputEvent->handle() };
    wchar_t inputBuffer[16];
    DWORD charsRead;

    while (true) {
        // 等待终止信号或输入请求
        DWORD waitResult = WaitForMultipleObjects(2, events, FALSE, INFINITE);
        if (waitResult == WAIT_OBJECT_0) {
            // 收到终止信号,退出线程
            break;
        }
        else if (waitResult == WAIT_OBJECT_0 + 1) {
            // 收到输入请求,提示用户输入
            wchar_t prompt[] = L"Option: ";
            WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), prompt, lstrlen(prompt), nullptr, nullptr);
            
            // 读取用户输入
            ReadConsole(GetStdHandle(STD_INPUT_HANDLE), inputBuffer, sizeof(inputBuffer)/sizeof(wchar_t)-1, &charsRead, nullptr);
            inputBuffer[charsRead-1] = L'\0'; // 去掉换行符

            // 转换输入为整数
            *threadArgs->option = _wtoi(inputBuffer);
            
            // 通知主线程输入完成
            threadArgs->inputEvent->set();
        }
    }
    return NO_ERROR;
}

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

    int option{};
    auto_event inputEvent;
    auto_event terminateEvent;
    ThreadArgs args{ &option, &inputEvent, &terminateEvent };

    // 创建单个输入线程,复用处理所有输入请求
    thread worker = thread::start(get_option, &args);

    while (true) {
        inputEvent.reset_event();
        // 唤醒输入线程处理输入
        inputEvent.set();

        if (inputEvent.wait(5000)) {
            // 清屏
            COORD topLeft = {0, 0};
            CONSOLE_SCREEN_BUFFER_INFO screenInfo;
            GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &screenInfo);
            DWORD screenSize = screenInfo.dwSize.X * screenInfo.dwSize.Y;
            FillConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), L' ', screenSize, topLeft, nullptr);
            SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), topLeft);

            switch (option) {
            case 1: {
                wchar_t msg[] = L"Option 1\n";
                WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), msg, lstrlen(msg), nullptr, nullptr);
                break;
            }
            case 2: {
                wchar_t msg[] = L"Option 2\n";
                WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), msg, lstrlen(msg), nullptr, nullptr);
                break;
            }
            case 3: // 退出程序
                terminateEvent.set();
                worker.join();
                goto exitLoop;
            default: {
                wchar_t msg[] = L"Option not valid!\n";
                WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), msg, lstrlen(msg), nullptr, nullptr);
                break;
            }
            }
        }
        else {
            // 超时,终止线程并退出
            terminateEvent.set();
            worker.join();
            wchar_t msg[] = L"***PROGRAM END***\n";
            WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), msg, lstrlen(msg), nullptr, nullptr);
            break;
        }
    }
exitLoop:
    return 0;
}

优化点验证

  • 线程安全:通过事件通知线程自行退出,避免TerminateThread的风险。
  • 资源高效:单个线程复用,减少线程创建销毁开销;修复了事件句柄的资源泄漏。
  • IO兼容性:使用WINAPI原生控制台函数,避免标准IO的同步问题。
  • 代码健壮性:类型安全的参数传递,修正了循环退出逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 05:27:38