Pregunta

Tengo una aplicación en la que un poco de procesamiento paralelo sería beneficioso. Para los fines de la discusión, digamos que hay un directorio con 10 archivos de texto, y quiero iniciar un programa, que bifurca 10 procesos, cada uno de los cuales toma uno de los archivos y pone en mayúscula el contenido del archivo. Reconozco que el programa principal puede esperar a que los niños completen usando una de las funciones esperar , o usando la función seleccionar .

Lo que me gustaría hacer es que el proceso principal supervise el progreso de cada proceso bifurcado y muestre algo así como una barra de progreso a medida que se ejecutan los procesos.

Mi pregunta.

¿Cuáles serían las alternativas razonables que tengo para que los procesos bifurcados comuniquen esta información a los padres? ¿Qué técnicas de IPC serían razonables de usar?

¿Fue útil?

Solución

En este tipo de situación en la que solo desea monitorear el progreso, la alternativa más fácil es usar memoria compartida. Cada proceso actualiza su valor de progreso (por ejemplo, un número entero) en un bloque de memoria compartida, y el proceso maestro lee el bloque regularmente. Básicamente, no necesita ningún bloqueo en este esquema. Además, es un "sondeo" aplicación de estilo porque el maestro puede leer la información cuando lo desee, por lo que no necesita ningún procesamiento de eventos para manejar los datos de progreso.

Otros consejos

Si el único progreso que necesita es "¿cuántos trabajos se han completado?", entonces un simple

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);
}

lo hará. Para informar sobre el progreso mientras, bueno, en progreso, aquí hay implementaciones tontas de algunas de las otras sugerencias.

Tuberías:

#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;
}

Memoria compartida:

#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;
}

Manejo de errores, etc.

Algunas opciones (no tengo idea de cuál, si alguna, le conviene, mucho depende de lo que realmente esté haciendo, según la analogía de los "mayúsculas y minúsculas"):

  • señales
  • fifos / tubos con nombre
  • el STDOUT de los niños u otros identificadores pasados ??
  • colas de mensajes (si corresponde)

Si todo lo que desea es una actualización de progreso, la forma más fácil es, con mucho, utilizar una tubería anónima. La llamada a la tubería (2) le dará dos descriptores de archivo, uno para cada extremo de la tubería. Llámalo justo antes de bifurcar, y haz que el padre escuche el primer fd y el niño escriba al segundo. (Esto funciona porque tanto los descriptores de archivo como la matriz de dos elementos que los contiene se comparten entre los procesos, no la memoria compartida en sí, pero es una copia en escritura, por lo que comparten los valores a menos que los sobrescriba).

Hace poco, alguien me dijo que siempre usan una tubería, por la cual los niños pueden enviar notificaciones al proceso principal de que todo va bien. Esta parece una solución decente, y es especialmente útil en lugares donde desea imprimir un error, pero ya no tiene acceso a stdout / stderr, etc.

Boost.MPI debería ser útil en este escenario. Puede considerarlo excesivo, pero definitivamente vale la pena investigarlo:
www.boost.org/doc/html/mpi.html

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top