如何同步父/子进程执行?

发布于 2025-01-08 01:23:05 字数 2109 浏览 2 评论 0原文

我想执行一个子进程并同步它(可能与互斥体),而不等待子进程终止:

Parent:

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.

Child:

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) 没有退出循环。
我做错了什么?

I would like to execute a child process and synchronize it (possibly with Mutex) without waiting for the child process to terminate:

Parent:

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.

Child:

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.

I'm expecting to see an output of:

Project1 : 1
Project2 : 2
Project1 : 3

Problem is that sometimes the Parent (Project1.exe) is not exiting the loop.
What am I doing wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

你列表最软的妹 2025-01-15 01:23:05

你在互斥锁上进行了一场竞赛。您希望出现以下顺序:

child:  create mutex
parent: open mutex
child:  destroy mutex

但可能发生的情况是,

child:  create mutex
child:  destroy mutex
parent: open mutex (fails because mutex is destroyed)

我不太清楚您的最终目标是什么,但我怀疑某个事件实际上就是您正在寻找的内容。

在父级中:

  1. 创建一个命名事件。
  2. 将事件设置为无信号。
  3. 创建子进程。
  4. 等待事件发出信号。

在孩子身上:

  1. 做一些处理。
  2. 打开指定的事件。
  3. 将事件设置为有信号,从而将父事件从等待中释放。

在非常高的级别,您需要的代码将如下所示:

Parent

Event = CreateEvent(nil, True, False, EventName);
//create it manual reset, set to non-signaled
ShellExecEx(....);
WaitForSingleObject(Event);

Child

Event = CreateEvent(nil, True, False, EventName);
//do stuff
SetEvent(Event);

我没有包含任何错误检查。我相信你可以添加一些。您可能还会发现 SyncObjs 中的事件包装类更方便。


最后,您的代码有一个繁忙的循环。这几乎永远不是解决任何问题的方法。如果您发现自己编写了一个繁忙的循环,您应该将其视为设计不正确的信号。重点是,在您的代码中,如果可以使其正常工作,父进程在等待子进程时将消耗 100% 的 CPU 利用率。

You have a race on the mutex. You are hoping for the following sequence:

child:  create mutex
parent: open mutex
child:  destroy mutex

But what can happen is

child:  create mutex
child:  destroy mutex
parent: open mutex (fails because mutex is destroyed)

I can't quite work out what your ultimate goal is but I have a suspicion that an event is actually what you are looking for.

In the parent:

  1. Create a named event.
  2. Set the event to non-signaled.
  3. Create the child process.
  4. Wait until the event is signaled.

In the child:

  1. Do some processing.
  2. Open the named event.
  3. Set the event to signaled, thereby releasing the parent from its wait.

In very high level the code you need will look like this:

Parent

Event = CreateEvent(nil, True, False, EventName);
//create it manual reset, set to non-signaled
ShellExecEx(....);
WaitForSingleObject(Event);

Child

Event = CreateEvent(nil, True, False, EventName);
//do stuff
SetEvent(Event);

I've not included any error checking. I'm sure you can add some. You may also find that the event wrapper class in SyncObjs is more convenient.


Finally, your code has a busy loop. That is almost never the solution to any problem. If ever you find yourself writing a busy loop you should take that as a signal that the design is incorrect. The point being that, in your code, if it could be made to work, the parent process would burn 100% CPU utilization whilst waiting on the child process.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文