【问题标题】:How to wait for 2 types of events in a loop (C)?如何在循环(C)中等待两种类型的事件?
【发布时间】:2016-12-11 05:11:19
【问题描述】:

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

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

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

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

【问题讨论】:

  • 你想用异步的方式来做吧?
  • 这更像是一个线程问题。您可以搜索如何在 C 中实现线程。
  • 请详细说明您的问题。什么是waitpid,你在读什么。特别是,您是否只是有一个子进程并想读取其输出但只需要知道它何时结束?

标签: c linux events concurrency condition-variable


【解决方案1】:

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

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

来自waitpid 的手册页:

WNOHANG - 如果没有孩子退出,则立即返回。

因此,如果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);

这只是假设您是来自套接字的reading,从代码中的变量名称来看。正如其他人所说,您的问题可能需要更繁重的任务,例如线程。

【讨论】:

  • 你能详细说明为什么你建议这样的事情而不是处理 SIGCHLD 吗?
  • 因为用户最初使用的是 waitpid,而不是处理信号,尽管这当然是一个有效的选项,因为他们的程序正在等待子进程。
  • cmon。在大多数情况下,为 SIGCHLD 安装处理程序并从读取中处理 EINTR 是标准的做法。所提出的解决方案较差,因为它向循环添加了更多系统调用,并且即使在无事可做时也使线程唤醒。更重要的是,目前还不清楚 OP 是否有任何理由首先在循环中等待。很可能他们只是生了一个孩子,只是想在孩子退出后停止阅读。解决方案是继续阅读直到没有任何内容,然后使用 waitpid 收集退出状态。
  • 这就是说,“他们没有处理信号”听起来不是一个正当的理由。他们也没有使用民意调查。可能无法在此处使用上述标准解决方案,并且您提供的代码可能用作合理的解决方法。不幸的是,在没有要求 OP 澄清问题的情况下给出了答案,类似于此处的其他答案。这至少不会直接进入线程。
  • 不必刻薄,我只是在拟合 OPs 问题的最明显解释。随意提出您自己的答案。
【解决方案2】:

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

这个想法很简单,不让waitpidread 函数等待,除非它们花费一些时间来执行它们的操作。这个想法是保持一个超时机制,这样,如果waitpid 对整个操作没有任何影响,它将立即返回,读取操作也是如此。

如果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;
}

【讨论】:

    【解决方案3】:

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

    您可以启动一个新线程并使用管道将其与原始线程连接。新线程将调用waitpid,完成后会将其结果写入管道。主线程将使用select 等待套接字或管道。

    【讨论】:

    • 请原谅我的无知,你能把这与使用 SIGCHLD 进行对比吗?您所展示的内容似乎涉及更多,并且没有提供我会看到的任何好处。此外,还不清楚 OP 是否首先需要任何类似的东西,很有可能他们实际上有一个管道,只需要等待它关闭,之后就可以等待 pid 来获取退出状态。
    猜你喜欢
    • 2022-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-01
    • 2021-07-02
    • 1970-01-01
    • 2019-05-19
    • 2012-09-06
    相关资源
    最近更新 更多