Дочерние процессы

При разработке приложения часто бывает нужно, чтобы какую-то операцию выполнял другой блок кода. Поэтому — хочешь, не хочешь — приходится постоянно вызывать функции или подпрограммы. Но вызов функции приводит к приостановке выполнения основного кода программы до возврата из вызванной функции Альтернативный способ — передать выполнение какой-то операции другому потоку в пределах данного процесса (поток, разумеется, нужно сначала создать). Это позволит основному коду программы продолжить работу в то время, как дополнительный поток будет выполнять другую операцию. Прием весьма удобный, но, когда основному потоку потребуется узнать результаты работы другого потока, не избежать проблем, связанных с синхронизацией.

Есть еще один прием: главный процесс порождает дочерний и возлагает на него выполнение части операций. Будем считать, что эти операции очень сложны. Допустим, для их реализации просто создается новый поток внутри того же процесса. Написан тот или иной код, при его тестировании получается некорректный результат — может, ошиблись в алгоритме или запутались в ссылках и случайно перезаписали какие-нибудь важные данные в адресном пространстве своего процесса. Так вот, один из способов защитить адресное пространство основного процесса от подобных ошибок как раз и состоит в том, чтобы передать часть работы отдельному процессу. Далее можно или подождать, пока он завершится, или продолжить работу параллельно с ним.

Если необходимо создать новый процесс и заставить его выполнить какие-либо опе рации и дождаться их результатов, можно использовать примерно такой код:

PROCESS_INFORMATION pi;DWORD dwExitCode; // Порождение дочернего процесса.BOOL fSuccess = CreateProcess(..., &pi);if (fSuccess) { // Закрытие дескриптора потока как только он больше не нужен CloseHandle(pi.hThread); // Приостанавливаем выполнение главного процесса до тех пор пока дочерний // не завершился WaitForSingleObject(pi.hProcess, INFINITE); // Дочерний процесс завершен; получаем его код завершения. GetExitCodeProcess(pi.hProcess, &dwExitCode); // Закрываем дескриптор так как он больше не нужен. CloseHandle(pi.hProcess);}

В этом фрагменте кода мы создали новый процесс и, если это прошло успешно, вызвали функцию WaitForSingleObject:

DWORD WaitForSingleObject(HANDLE hObject, DWORD dwTimeOut);

Функция задерживает выполнение кода до тех пор, пока объект, определяемый параметром hObject, не перейдет в свободное (незанятое) состояние. Объект «процесс» переходит в такое состояние при его завершении По этому вызов WaitForSingleObject приостанавливает выполнение потока родительского процесса до завершения порожденного им процесса. Когда WaitForSingleObject вернет управление, можно узнаеть код завершения дочернего процесса через функцию Get ExitCodeProcess.

Обращение к CloseHandle в приведенном выше фрагменте кода заставляет систему уменьшить значения счетчиков объектов «поток» и "процесс" до нуля и тем самым освободить память, занимаемую этими объектами.

Нетрудно заметить, что в этом фрагменте описатель объекта ядра "первичный поток" (принадлежащий дочернему процессу) был закрыт сразу после возврата из CreateProcess. Это не приводит к завершению первичного потока дочернего процесса — просто уменьшает счетчик, связанный с упомянутым объектом. А вот почему это делается — и, кстати, даже рекомендуется делать — именно так, станет ясно из про стого примера. Допустим, первичный поток дочернего процесса порождает еще один поток, а сам после этого завершается. В этот момент система может высвободить объект "первичный поток" дочернего процесса из памяти, если у родительского процесса нет описателя данного объекта. Но если родительский процесс располагает таким описателем, система не сможет удалить этот объект из памяти до тех пор, пока и родительский процесс не закроет его описатель.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: