Как синхронизировать выполнение родительского / дочернего процесса?
Я хотел бы выполнить дочерний процесс и синхронизировать его (возможно, с мьютексом), не дожидаясь завершения дочернего процесса:
Родитель:
program Project1;
{$APPTYPE CONSOLE}
uses
Windows, ShellApi, SysUtils, Dialogs;
procedure ShellExecEx(Wnd: HWND; const AExeFilename, AParams: string);
const
SEE_MASK_NOZONECHECKS = $00800000;
SEE_MASK_WAITFORINPUTIDLE = $02000000;
SEE_MASK_NOASYNC = $00000100;
var
Info: TShellExecuteInfo;
begin
FillChar(Info, SizeOf(Info), 0);
Info.Wnd := Wnd;
Info.cbSize := SizeOf(Info);
Info.fMask := SEE_MASK_FLAG_NO_UI or SEE_MASK_NOZONECHECKS or
SEE_MASK_NOASYNC
//or SEE_MASK_WAITFORINPUTIDLE (works only with UI app ???)
//or SEE_MASK_NO_CONSOLE
//or SEE_MASK_NOCLOSEPROCESS
;
Info.lpVerb := '';
Info.lpFile := PChar(AExeFilename);
Info.lpParameters := PChar(AParams);
Info.lpDirectory := PChar(ExtractFilePath(AExeFilename));
Info.nShow := SW_SHOWNORMAL;
if not ShellExecuteEx(@Info) then
RaiseLastOSError;
CloseHandle(Info.hProcess);
end;
var
Mutex: THandle = 0;
Error: DWORD;
begin
OutputDebugString('Project1 : 1');
ShellExecEx(0, 'Project2.exe', '');
// synchronize
repeat
// attempt to create a named mutex
Mutex := CreateMutex(nil, False, 'F141518A-E6E4-4BC0-86EB-828B1BC48DD1');
Error := GetLastError;
if Mutex = 0 then RaiseLastOSError;
CloseHandle(Mutex);
until Error = ERROR_ALREADY_EXISTS;
OutputDebugString('Project1 : 3');
end.
Ребенок:
program Project2;
{$APPTYPE CONSOLE}
uses
SysUtils, Windows, Dialogs;
var
Mutex: THandle = 0;
begin
OutputDebugString('Project2 : 2');
// attempt to create a named mutex and acquire ownership
Mutex := CreateMutex(nil, True, 'F141518A-E6E4-4BC0-86EB-828B1BC48DD1');
if Mutex = 0 then RaiseLastOSError;
// do something
ReleaseMutex(Mutex);
CloseHandle(Mutex); // <- at this point Program1.exe should exit the repeat loop
ShowMessage('ok from Project2');
end.
Я ожидаю увидеть результат:
Project1 : 1
Project2 : 2
Project1 : 3
Проблема в том, что иногда Родитель (Project1.exe) не выходит из цикла.
Что я делаю не так?
1 ответ:
У вас есть гонка на мьютексе. Вы надеетесь на следующую последовательность:
child: create mutex parent: open mutex child: destroy mutex
Но что может случиться-это
child: create mutex child: destroy mutex parent: open mutex (fails because mutex is destroyed)
Я не могу точно определить, какова ваша конечная цель, но у меня есть подозрение, что событие-это на самом деле то, что вы ищете.В родителе:
- создайте именованное событие.
- установите событие в состояние без сигнала.
- создайте дочерний процесс.
- дождитесь сигнала о наступлении события.
В ребенок:
- сделайте некоторую обработку.
- откройте именованное событие.
- установите событие в signaled,тем самым освободив родителя от ожидания.
На очень высоком уровне код, который вам нужен, будет выглядеть следующим образом:
Родитель
Event = CreateEvent(nil, True, False, EventName); //create it manual reset, set to non-signaled ShellExecEx(....); WaitForSingleObject(Event);
Ребенок
Event = CreateEvent(nil, True, False, EventName); //do stuff SetEvent(Event);
Я не включил проверку ошибок. Я уверен, что вы можете добавить немного. Вы также можете обнаружить, что класс-оболочка событий в
SyncObjs
более удобен.
Наконец, ваш код имеет цикл занятости. Это почти никогда не является решением какой-либо проблемы. Если вы когда-нибудь обнаружите, что пишете цикл занятости, вы должны принять это как сигнал о том, что дизайн неверен. Дело в том, что в вашем коде, если его можно заставить работать, родительский процесс будет сжигать 100% загрузки процессора во время ожидания дочернего процесса.