Как дождаться завершения всех дочерних процессов, вызываемых fork ()? - PullRequest
18 голосов
/ 11 ноября 2008

Я разрабатываю несколько процессов и хочу измерить, сколько времени требуется для выполнения всей задачи, то есть когда все разветвленные процессы завершены. Посоветуйте, пожалуйста, как заставить родительский процесс ждать завершения всех дочерних процессов? Я хочу убедиться, что я остановил таймер в нужный момент.

Вот код, который я использую:

#include <iostream>
#include <string>
#include <fstream>
#include <sys/time.h>
#include <sys/wait.h>

using namespace std;

struct timeval first,  second,  lapsed;
struct timezone tzp; 

int main(int argc, char* argv[])// query, file, num. of processes.
{

    int pCount = 5; // process count

    gettimeofday (&first, &tzp); //start time

    pid_t* pID = new pid_t[pCount];

    for(int indexOfProcess=0; indexOfProcess<pCount; indexOfProcess++)
    {
        pID[indexOfProcess]= fork();

        if (pID[indexOfProcess] == 0)                // child
        {
            // code only executed by child process

            // magic here

            // The End
            exit(0);
        }
        else if (pID[indexOfProcess] < 0)    // failed to fork
        {
            cerr << "Failed to fork" << endl;
            exit(1);
        }
        else                         // parent
        {
            // if(indexOfProcess==pCount-1) and a loop with waitpid??

            gettimeofday (&second, &tzp); //stop time
            if (first.tv_usec > second.tv_usec)
            {
                second.tv_usec += 1000000;
                second.tv_sec--;
            }

            lapsed.tv_usec = second.tv_usec - first.tv_usec;
            lapsed.tv_sec = second.tv_sec - first.tv_sec; 

            cout << "Job performed in " <<lapsed.tv_sec << " sec and " << lapsed.tv_usec    << " usec"<< endl << endl;

        }

    }//for

}//main

Ответы [ 5 ]

22 голосов
/ 11 ноября 2008

Я бы переместил все после строки "else // parent" вниз, вне цикла for. После цикла вилок выполните другой цикл for с waitpid, затем остановите часы и сделайте все остальное:

for (int i = 0; i < pidCount; ++i) {
    int status;
    while (-1 == waitpid(pids[i], &status, 0));
    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        cerr << "Process " << i << " (pid " << pids[i] << ") failed" << endl;
        exit(1);
    }
}

gettimeofday (&second, &tzp); //stop time

Я предположил, что если дочерний процесс не может нормально завершиться со статусом 0, то он не завершил свою работу, и поэтому тест не смог получить действительные данные синхронизации. Очевидно, что если дочерние процессы предположительно будут уничтожены сигналами или выйдут из состояния возврата, отличного от 0, вам придется соответствующим образом изменить проверку ошибок.

Альтернатива с использованием wait:

while (true) {
    int status;
    pid_t done = wait(&status);
    if (done == -1) {
        if (errno == ECHILD) break; // no more child processes
    } else {
        if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
            cerr << "pid " << done << " failed" << endl;
            exit(1);
        }
    }
}

Этот не говорит вам, какой процесс в последовательности завершился неудачно, но если вам не все равно, вы можете добавить код, чтобы найти его в массиве pids и вернуть индекс.

13 голосов
/ 11 ноября 2008

Самый простой способ сделать

while(wait() > 0) { /* no-op */ ; }

Это не будет работать, если wait() не получится по какой-то причине, кроме того, что не осталось детей. Так что с некоторой проверкой ошибок это становится

int status;
[...]
do {
    status = wait();
    if(status == -1 && errno != ECHILD) {
        perror("Error during wait()");
        abort();
    }
} while (status > 0);

См. Также страницу руководства wait(2).

4 голосов
/ 11 ноября 2008

Ожидание вызова (или waitpid) в цикле, пока не будут учтены все дочерние элементы.

В этом случае все процессы в любом случае синхронизируются, но в общем случае предпочтение отдается ожиданию, когда можно выполнить больше работы (например, пул рабочих процессов), поскольку оно будет возвращаться при изменении первого доступного состояния процесса.

3 голосов
/ 11 ноября 2008

Я полагаю, что системный вызов ожидания выполнит то, что вы ищете.

0 голосов
/ 11 ноября 2008
for (int i = 0; i < pidCount; i++) {
    while (waitpid(pids[i], NULL, 0) > 0);
}

Он не будет ждать в правильном порядке, но остановится вскоре после смерти последнего ребенка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...