4

我正在开发一个 Windows 应用程序,它有一个单独的线程,用于通过标准输入处理用户(或第 3 方)应用程序输入。

这个线程被设计成它通过WaitForMultipleObjects两个事件等待:

  1. 一个死亡信号。当这个信号产生时,接口处理线程关闭。
  2. 一个接口信号。当这个信号被提升时,有输入准备好被读取。输入被读取和处理。

在 Windows 下,该线程进入一个主循环,Wait用于这两个事件(其中bWaitAllis FALSE)。等待 stdin 句柄的作用是在有输入准备好读取时发出信号,而另一个事件是从应用程序的其他地方设置的。

这完全符合我的要求。它在不进入忙等待的情况下等待引发事件,并且同时等待这两个事件。

我希望将此功能移植到 Linux,但我不确定如何达到预期的效果。从根本上说,我真正想要的是:

在 Linux 下,我如何设计一个线程,以便它立即响应标准输入上的用户输入,但它也可以立即响应从应用程序的其他地方引发的终止标志?

为了完成后者,在我看来,在用户输入文本之前,我不能使用,cin或任何其他阻塞的功能。然而,我不知道如何在基于控制台的应用程序中不阻塞地读取用户输入。getsgetch

我对架构的任何更改(如果有更多的 Linux-y 方式来做到这一点)持开放态度,其中包括在一个单独的线程中处理用户输入,该线程可以从应用程序的其他地方终止。我正在使用 GCC 4.4 和 Boost 1.51。

4

2 回答 2

3

在 Linux 中执行此操作的标准方法是使用select(2)系统调用。但是,select比 更受限制WaitForMultipleObjects,因为它只能等待文件描述符,而不是其他类型的对象(例如事件)。因此,解决这个问题的典型方法是创建一个管道并将一个虚拟值写入管道作为您的“信号”。

像这样的东西:

// Error checking omitted for expository purposes
int pipefd[2];
pipe(pipefd);  // Create the pipe

while(1)
{
    // Create file descriptor set of stdin and the read end of the pipe
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(STDIN_FILENO, &fds);
    FD_SET(pipefd[0], &fds);
    int maxfd = MAX(STDIN_FILENO, pipefd[0]);

    // Wait until input becomes available on either stdin or the pipe
    int num_available = select(&fds, NULL, NULL, NULL);

    // Read & process stdin if possible (will not block)
    if (FD_ISSET(STDIN_FILENO, &fds))
    {
        int n = read(STDIN_FILENO, buffer, size);
        ...
    }

    // Read & process pipe if possible (will not block)
    if (FD_ISSET(pipefd[0], &fds))
    {
        char dummy;
        read(pipefd[0], &dummy, 1);
        // Handle signal (e.g. break out of loop)
    }
}

然后向线程发出信号表明它已经完成,只需将一个字节写入管道的写入端:

char dummy = 42;
write(pipefd[1], &dummy, 1);
于 2012-11-06T21:49:28.450 回答
1

libev(以及几个类似的化身)提供了一个方便的抽象,select包括能够在signals 上挂起。

如果您可以选择更改“接口信号”的来源,那么您可以考虑将其更改为使用raise

于 2012-11-06T21:58:56.747 回答