3

我有一个应用程序,其中一些并行处理会有所帮助。出于讨论的目的,假设有一个目录,其中包含 10 个文本文件,我想启动一个程序,该程序分叉 10 个进程,每个进程获取一个文件,并将文件内容大写。我承认父程序可以使用等待函数之一或使用select函数等待子程序完成。

我想做的是让父进程监视每个分叉进程的进度,并在进程运行时显示进度条之类的东西。

我的问题。

对于分叉的进程将这些信息传回给父进程,我有什么合理的替代方案?使用哪些 IPC 技术是合理的?

4

6 回答 6

2

在这种只想监控进度的情况下,最简单的选择是使用共享内存。每个进程在共享内存块上更新它的进度值(例如整数),并且主进程定期读取该块。基本上,您不需要在此方案中进行任何锁定。此外,它是一个“轮询”风格的应用程序,因为主控器可以随时读取信息,因此您不需要任何事件处理来处理进度数据。

于 2009-03-17T00:09:55.953 回答
2

如果您需要的唯一进展是“完成了多少工作?”,那么一个简单的

while (jobs_running) {
    pid = wait(&status);
    for (i = 0; i < num_jobs; i++)
        if (pid == jobs[i]) {
            jobs_running--;
            break;
        }
    printf("%i/%i\n", num_jobs - jobs_running, num_jobs);
}

会做。为了在进行中报告进度,这里是其他一些建议的愚蠢实现。

管道:

#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

int child(int fd) {
    int i;
    struct timespec ts;
    for (i = 0; i < 100; i++) {
        write(fd, &i, sizeof(i));
        ts.tv_sec = 0;
        ts.tv_nsec = rand() % 512 * 1000000;
        nanosleep(&ts, NULL);
    }
    write(fd, &i, sizeof(i));
    exit(0);
}

int main() {
    int fds[10][2];
    int i, j, total, status[10] = {0};
    for (i = 0; i < 10; i++) {
        pipe(fds[i]);
        if (!fork())
            child(fds[i][1]);
    }
    for (total = 0; total < 1000; sleep(1)) {
        for (i = 0; i < 10; i++) {
            struct pollfd pfds = {fds[i][0], POLLIN};
            for (poll(&pfds, 1, 0); pfds.revents & POLLIN; poll(&pfds, 1, 0)) {
                read(fds[i][0], &status[i], sizeof(status[i]));
                for (total = j = 0; j < 10; j++)
                    total += status[j];
            }
        }
        printf("%i/1000\n", total);
    }
    return 0;
}

共享内存:

#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>

int child(int *o, sem_t *sem) {
    int i;
    struct timespec ts;
    for (i = 0; i < 100; i++) {
        sem_wait(sem);
        *o = i;
        sem_post(sem);
        ts.tv_sec = 0;
        ts.tv_nsec = rand() % 512 * 1000000;
        nanosleep(&ts, NULL);
    }
    sem_wait(sem);
    *o = i;
    sem_post(sem);
    exit(0);
}

int main() {
    int i, j, size, total;
    void *page;
    int *status;
    sem_t *sems;
    size = sysconf(_SC_PAGESIZE);
    size = (10 * sizeof(*status) + 10 * sizeof(*sems) + size - 1) & size;
    page = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
    status = page;
    sems = (void *)&status[10];
    for (i = 0; i < 10; i++) {
        status[i] = 0;
        sem_init(&sems[i], 1, 1);
        if (!fork())
            child(&status[i], &sems[i]);
    }
    for (total = 0; total < 1000; sleep(1)) {
        for (total = i = 0; i < 10; i++) {
            sem_wait(&sems[i]);
            total += status[i];
            sem_post(&sems[i]);
        }
        printf("%i/1000\n", total);
    }
    return 0;
}

为清楚起见,省略了错误处理等。

于 2009-03-17T21:58:05.210 回答
1

一些选项(不知道哪个,如果有的话,会适合你——很大程度上取决于你实际在做什么,与“大写文件”的类比相反):

  • 信号
  • fifos / 命名管道
  • 孩子的 STDOUT 或其他传递的句柄
  • 消息队列(如果适用)
于 2009-03-16T23:22:58.577 回答
1

如果您想要的只是进度更新,那么到目前为止最简单的方法可能是使用匿名管道。pipe(2) 调用将为您提供两个文件描述符,一个用于管道的每一端。在你 fork 之前调用它,让父母听第一个 fd,孩子写第二个。(这是有效的,因为文件描述符和包含它们的双元素数组都是在进程之间共享的——本身不是共享内存,但它是写时复制,所以除非你覆盖它们,否则它们共享值。)

于 2009-03-17T01:33:57.873 回答
0

就在今天早些时候,有人告诉我他们总是使用管道,子进程可以通过管道向父进程发送一切进展顺利的通知。这似乎是一个不错的解决方案,并且在您想要打印错误但不再能够访问 stdout/stderr 等的地方特别有用。

于 2009-03-16T23:20:26.463 回答
0

Boost.MPI在这种情况下应该很有用。您可能会认为它矫枉过正,但绝对值得研究:
www.boost.org/doc/html/mpi.html

于 2009-03-17T01:26:23.507 回答