常州企业微信网站建设,wordpress 折叠菜单,大兴安岭做网站,wordpress自动推送工具代码文章目录 背景介绍代码示例版本1-基本命令处理版本2-多线程命令处理#xff0c;不阻塞主函数版本3-即使没有用户输入#xff0c;也能立即退出 背景介绍
在开发命令行工具或控制台应用程序时#xff0c;经常需要处理用户输入。常规做法是使用标准输入#xff08;stdin… 文章目录 背景介绍代码示例版本1-基本命令处理版本2-多线程命令处理不阻塞主函数版本3-即使没有用户输入也能立即退出 背景介绍
在开发命令行工具或控制台应用程序时经常需要处理用户输入。常规做法是使用标准输入stdin来获取用户输入的数据。常见的有C语言的getchar()、C的std::getline()以及Windows API的ReadFile()等函数这些操作通常是阻塞的它们会挂起调用线程直到有数据可读或者发生某种错误。然而在某些应用场景中可能需要在没有用户输入的情况下优雅地退出应用程序。例如当系统需要进行紧急停止或转入其他任务时如果输入线程因等待getchar()或std::getline()而阻塞则无法立即响应。这种情况下需要一种机制来使得阻塞在标准输入上的读取操作能够立即返回从而允许程序继续执行或安全退出。接下来将介绍一种实现方案旨在解决如何让标准输入的阻塞读取在必要时能够立即返回以提高程序的响应能力和用户体验。
代码示例
版本1-基本命令处理
std::cout Enter commands (read or write). Type exit to quit: std::endl;
std::string command;
while (std::getline(std::cin, command)) {if (command exit) {break; // 用户输入exit时退出循环}else if (command read) {std::cout Reading data... std::endl;}else if (command write) {std::cout Writing data... std::endl;}else {std::cout Unknown command. Please try read or write. std::endl;}
}版本2-多线程命令处理不阻塞主函数
std::atomic_bool keep_running true; // 控制线程运行的原子标志
// 创建一个处理输入的线程
std::thread input_thread([]() {std::cout Enter commands (read or write). Type exit to quit: std::endl;std::string command;// 循环读取命令直到收到停止信号while (keep_running std::getline(std::cin, command)) {if (command exit) {keep_running false; // 设置标志以终止循环break;}else if (command read) {std::cout Reading data... std::endl;}else if (command write) {std::cout Writing data... std::endl;}else {std::cout Unknown command. Please try read or write. std::endl;}}});std::this_thread::sleep_for(std::chrono::seconds(10)); // 模拟等待一段时间或等待其他事件触发
keep_running false; // 通知线程停止
input_thread.join(); // 等待线程完成相较于版本1版本2通过增加多线程处理避免了主函数的阻塞并使用了原子标志位以便用户可以控制何时退出线程。但仍存在不足由于输入线程依赖于 std::getline 的阻塞性质它必须等到接收到用户输入后才能检查退出条件因此程序的完全退出还需要依赖于用户输入用户不输入任何内容即使修改了标志位也无法让线程退出。
版本3-即使没有用户输入也能立即退出
// 使用原子标志来控制线程是否应该继续运行。
std::atomic_bool keep_running true;
// 获取标准输入的句柄。
HANDLE stdin_handle GetStdHandle(STD_INPUT_HANDLE);// 创建一个处理输入的线程。
std::thread input_thread([]() {std::cout Enter commands (read or write). Type exit to quit: std::endl;// 循环读取命令直到收到停止信号。while (keep_running) {char buffer[256] { 0 };DWORD bytes_read 0;// 阻塞地从标准输入读取数据。if (ReadFile(stdin_handle, buffer, sizeof(buffer) - 1, bytes_read, nullptr)) {buffer[bytes_read] \0; // 确保字符串是以null终止的。std::string command(buffer);// 解析命令并执行相应操作。if (command exit) {keep_running false; // 设置标志以终止循环。break;}else if (command read) {std::cout Reading data... std::endl;}else if (command write) {std::cout Writing data... std::endl;}else {std::cout Unknown command. Please try read or write. std::endl;}}else {// 如果ReadFile失败则输出失败消息。// 此处假设失败是因为主动取消ReadFile。std::cout ReadFile failure, because we want it exit. std::endl;}}});// 主线程等待一段时间或等待其他事件触发。
std::this_thread::sleep_for(std::chrono::seconds(10));
// 通知输入线程停止。
keep_running false;
// 取消所有挂起的I/O操作以确保ReadFile可以退出。
CancelIoEx(stdin_handle, nullptr);
// 等待输入线程完成。
input_thread.join();在版本3中引入了 CancelIoEx 函数这个函数用于取消指定文件句柄上所有未完成的输入/输出I/O操作。调用 CancelIoEx 后所有挂起的 I/O 操作会被取消任何阻塞的 ReadFile 调用都将返回失败。这种失败的检测使我们能够在不等待用户输入的情况下让 ReadFile 从阻塞状态返回。随后程序会检查循环条件例如 keep_running 标志从而实现立即终止。为什么不使用 OVERLAPPED 结构和 WaitForMultipleObjects 同步属性的标准输入标准输入stdin的句柄在大多数情况下是同步的并且不支持真正的异步操作。尽管可以采用 OVERLAPPED 结构来异步读取实际上 ReadFile 仍然表现为阻塞调用这意味着无法通过事件来判断其可读状态。简单性与需求满足当前使用同步阻塞调用已足够满足我们的需求即能够实现快速退出。这种同步调用方法在逻辑上更加简单直接。