【问题标题】:Possible to stop cin from waiting input?可以阻止cin等待输入吗?
【发布时间】:2012-01-24 00:54:06
【问题描述】:

在图形应用程序中,我使用控制台输入执行调试命令。创建控制台时,还会创建一个新线程来收集处理所有输入的用户命令,图形应用程序将继续并行运行。我使用 boost::thread 库。

到目前为止它工作得很好,但是我还没有找到一个很好的解决方案来停止这个线程的执行。线程一直在等待用户输入:

 while(appRunning)
 {
     std::cin>>theUserCommand;
     // ...do stuff
 }

然后当图形应用程序结束时,它将停止所有控制台功能,其中我包含线程:

 appRunning = false;
 // do some more related clean up
 myListeningThread->join();

如您所见,在调用连接之后,std::cin 将等待用户输入。 我尝试的解决方案之一是创建“合成击键”事件,std::cin 将获得您使用 ENTER 发送的任何值,线程将很好地结束,这个解决方案很糟糕,我不想保留它。 此外,它确实在执行该工具的环境之一中工作,但是当我尝试将它与 UI API 一起使用时失败了。你们能指导我如何以正确的方式解决这个问题吗?不能确定 C++ 文档中是否有一个函数可以停止 std::cin 等待用户输入,并继续执行程序,这是否可能?

编辑:很好,我发现 keybd_event 对于某些环境有点误导,使用 WriteConsoleInput 显式指定输入处理程序效果很好。

【问题讨论】:

  • 有几种可能性,但它们都依赖于操作系统。该程序适用于什么操作系统?
  • Win/VStudio9.0,我使用keybd_event作为我提到的解决方案,如果它依赖于操作系统是没有问题的。你会建议我如何解决它?
  • 顺便说一句,SetConsoleCtrlHandler 对我不起作用,这将结束应用程序进程,跳过所有清理以结束应用程序。
  • close(int),它只是被称为_close,没有任何理由(是的,我不得不将POSIX-API代码移植到Windows,你为什么要问?)但我不会不要指望这会做任何建设性的事情。实际系统调用的等价物是CloseHandle(GetStdHandle(STD_INPUT_HANDLE))) 和......我不知道这会对从同一个句柄读取的不同线程产生什么影响。试试看并告诉我们!
  • 嗯,如果您需要 Vista 或更高版本,CancelSynchronousIo 在这里可能会很有帮助。

标签: c++ windows iostream blocking boost-thread


【解决方案1】:

我不是一个 Windows 程序员,但我对 Unix 了解更多。我完全不熟悉boost::thread。也就是说,根据this MSDN page 底部的建议,这是我的建议:

  • 在创建控制台读取线程之前创建一个event object
  • 如果要关闭,请在调用线程的->join 方法之前立即对事件对象调用SetEvent
  • 将您的控制台读取线程中的主循环更改为阻塞WaitForMultipleObjects 而不是istream::operator>>,如下所示:

    for (;;) {
        HANDLE h[2];
        h[0] = GetStdHandle(STD_INPUT_HANDLE);
        h[1] = that_event_object_I_mentioned;
        DWORD which = WaitForMultipleObjects(2, h, FALSE, INFINITE);
    
        if (which == WAIT_OBJECT_0)
            processConsoleCommand();
        else if (which == WAIT_OBJECT_0 + 1)
            break;
        else
            abort();
    }
    
  • 此线程必须注意不要执行除WaitForMultipleObjects 调用之外的任何阻塞操作。根据下面 cmets 中的讨论,这意味着 processConsoleCommand 根本不能使用 cin。您需要改用low-level console input functions,尤其是GetNumberOfConsoleInputEventsReadConsoleInput,以确保您不会阻止;您将需要在多次调用processConsoleCommand 中累积字符,直到您读取回车;你还需要自己做回声。

【讨论】:

  • 请注意,使用WaitForMultipleObjects() 阻塞标准输入以避免阻塞读取从根本上讲是不可靠的。如果标准输入是键盘(CONIN$ 设备),那么按下任何键(即使是那些不产生输入的键,例如 CTRL)将解除阻塞等待,只等待无限期地阻塞下一个输入操作,从而导致线程忽略事件的状态。查看related question 了解更多详情。
  • 当用户输入时,这个线程是否在WaitForMultipleObjects 上旋转并不重要,只要在用户时它可以正常空闲打字。我想有一些等效的FIONREAD 可以用来确保ReadFile 不会阻塞,但是Windows 让我惊讶于之前没有类似的设施。如果不能使这样的事情起作用,我想我会考虑使用 GUI API 实现我自己的控制台窗口。
  • ... 该相关问题中的讨论并没有真正帮助;建议似乎是“使用专用线程从控制台读取”,在这里我们正在讨论如何精确地实现这样的线程。
  • 就是这样。我对这段代码有一个问题,按下CTRL+TABALT+TAB 会解除等待,然后std::cin 上的下一个操作会阻塞,因为控制台缓冲区中没有真正的输入。而且您没有为标准输入实现专用循环,因为您正在等待标准输入 and 和事件。
  • 在我看来,这个问题可以通过使用低级 Win32 控制台输入函数而不是 cin 来解决。更多的工作,是的。以及如何否则让您的专用控制台读取线程退出,缺少TerminateThread,出于显而易见的原因,这是一个坏主意?毕竟,这就是最初提出的问题。
猜你喜欢
  • 1970-01-01
  • 2017-05-03
  • 2023-03-10
  • 1970-01-01
  • 2018-06-01
  • 1970-01-01
  • 2015-07-18
  • 1970-01-01
  • 2017-01-01
相关资源
最近更新 更多