如何优化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
相关产品推荐
相关产品推荐

