如何在循环中等待两种类型的事件(C)?

How to wait for 2 types of events in a loop (C)?

我试图在 while-true 循环中等待 waitpid()read()。具体来说,我等待这两个事件中的任何一个,然后在循环的每次迭代中处理它。目前,我有以下实现(这不是我想要的)。

while (true) {
  pid_t pid = waitpid(...);
  process_waitpid_event(...);

  ssize_t sz = read(socket, ....);
  process_read_event(...);
}

这个实现的问题是第二个事件的处理依赖于第一个事件的完成。我不想按顺序处理这两个事件,而是希望处理在循环的每次迭代中首先出现的事件。我应该怎么做?

如果你不想接触线程,你可以在调用 waitpid 的选项中包含它:

pid_t pid = waitpid(pid, &status, WNOHANG);

来自 waitpid 的联机帮助页:

WNOHANG - return immediately if no child has exited.

因此,如果 waitpid 没有准备好,它不会阻塞,程序会继续转到下一行。

至于read,如果它阻塞了你可能想看看poll(2)。您基本上可以检查您的套接字是否在每个设定的时间间隔准备就绪,例如250ms,到时候再调用read。这将允许它不阻塞。

您的代码可能看起来有点像这样:

// Creating the struct for file descriptors to be polled.
struct pollfd poll_list[1];
poll_list[0].fd = socket_fd;
poll_list[0].events = POLLIN|POLLPRI;
// POLLIN  There is data to be read
// POLLPRI There is urgent data to be read

/* poll_res  > 0: Something ready to be read on the target fd/socket.
** poll_res == 0: Nothing ready to be read on the target fd/socket.
** poll_res  < 0: An error occurred. */
poll_res = poll(poll_list, 1, POLL_INTERVAL);

这只是假设您 read 从套接字,根据代码中的变量名来判断。正如其他人所说,您的问题可能需要一些更重的任务,例如线程。

如果您不想在程序中使用线程,@DanielPorteous 的答案也应该有效。

思路很简单,不是让waitpidread函数一直等待,除非它们耗费一些时间来完成它们的操作。这个想法是保持超时机制,这样,如果 waitpid 对整个操作没有任何影响,它会立即 return 并且同样的事情也适用于读取操作。

如果 read 函数读取整个缓冲区需要很长时间,您可以从 read 函数中手动限制读取,这样它就不会一次读取整个缓冲区,而是它读取 2 毫秒,然后将循环传递给 waitpid 函数执行。

但是为您的目的使用线程是安全的,而且它很容易实现。 Here's a nice guideline 关于如何实现线程。

在您的情况下,您需要声明两个线程。

pthread_t readThread;
pthread_t waitpidThread;

现在您需要创建线程并将特定函数作为参数传递。

pthread_create(&(waitpidThread), NULL, &waitpidFunc, NULL);
pthread_create(&(readThread), NULL, &readFunc, NULL);

现在您可能必须编写 waitpidFuncreadFunc 函数。他们可能看起来像这样。

void* waitpidFunc(void *arg)
{
    while(true) {
        pid_t pid = waitpid(...);

        // This is to put an exit condition somewhere. 
        // So that you can finish the thread
        int exit = process_waitpid_event(...);

        if(exit == 0) break;
    }

    return NULL;
}

我认为在这种情况下正确的工具是 selectpoll。两者本质上都在做同样的工作。它们允许 select 那些有输入可用的描述符。因此,例如,您可以同时等待两个套接字。但是,它不能直接用于您的情况,因为您要等待进程和套接字。解决方案是创建一个管道,在 waitpid 完成时接收一些东西。

您可以启动一个新线程,并用管道将它与原来的线程连接起来。新线程将调用 waitpid 并在完成后将其结果写入管道。主线程将使用 select.

等待套接字或管道