Question

J'ai une application où un peu de traitement parallèle serait bénéfique. Pour les besoins de la discussion, supposons qu’il existe un répertoire contenant 10 fichiers texte et que je souhaite lancer un programme qui renonce à 10 processus, chacun prenant l’un des fichiers et mettant en majuscule le contenu du fichier. Je reconnais que le programme parent peut attendre que les enfants aient terminé l’utilisation de l’une des fonctions wait ou de la fonction Select .

Ce que j'aimerais faire, c'est que le processus parent surveille la progression de chaque processus créé et affiche quelque chose comme une barre de progression pendant l'exécution des processus.

Ma question.

Quelles seraient les solutions raisonnables pour que les processus définis transmettent ces informations au parent? Quelles techniques IPC serait-il raisonnable d'utiliser?

Était-ce utile?

La solution

Dans ce genre de situation où vous souhaitez uniquement surveiller la progression, l'alternative la plus simple consiste à utiliser la mémoire partagée. Chaque processus met à jour sa valeur de progression (par exemple, un entier) sur un bloc de mémoire partagée, et le processus maître lit le bloc régulièrement. En gros, vous n'avez besoin d'aucun verrouillage dans ce schéma. En outre, il s'agit d'un "sondage". application de style, car le maître peut lire les informations à tout moment, vous n’avez donc besoin d’aucun traitement d’événement pour gérer les données de progression.

Autres conseils

Si le seul progrès dont vous avez besoin est "Combien de travaux ont été terminés?", puis 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);
}

va faire. Pour rendre compte des progrès réalisés, voici des mises en œuvre stupides de certaines des autres suggestions.

Tuyaux:

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

Mémoire partagée:

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

Traitement des erreurs, etc. pour des raisons de clarté.

Quelques options (aucune idée de ce qui vous conviendra le mieux - beaucoup dépend de ce que vous faites réellement, contrairement à l'analogie "mise en majuscule"):

  • signaux
  • fifos / tubes nommés
  • le STDOUT des enfants ou d'autres poignées transmises
  • files d'attente de messages (le cas échéant)

Si vous souhaitez uniquement une mise à jour de la progression, le moyen le plus simple est de loin d'utiliser un canal anonyme. L'appel pipe (2) vous donnera deux descripteurs de fichier, un pour chaque extrémité du tube. Appelez-le juste avant le fork et demandez au parent d'écouter le premier fd et à l'enfant d'écrire au second. (Cela fonctionne car les descripteurs de fichier et le tableau à deux éléments qui les contient sont partagés par les processus - pas par la mémoire partagée, mais par une copie sur écriture afin qu'ils partagent les valeurs, sauf si vous les écrasez.)

Un peu plus tôt dans la journée, une personne m'a dit qu'elle utilisait toujours un canal permettant aux enfants d'envoyer une notification au processus parent afin de lui indiquer que tout se passait bien. Cela semble une solution décente et est particulièrement utile dans les endroits où vous voudriez imprimer une erreur, mais n’avez plus accès à stdout / stderr, etc.

Boost.MPI devrait être utile dans ce scénario. Vous pensez peut-être que vous en faites trop, mais cela vaut vraiment la peine d'enquêter:
www.boost.org/doc/html/mpi.html

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top