如果进程数量相对较少,您可以为每个进程创建一个名为EventWaitHandle,并且创建等待句柄的顺序将定义允许打印进程的顺序。(请参阅下面的“稍后添加”部分,该部分描述了所提供代码中的竞争条件并讨论了修复。)
当一个进程启动时,它会尝试创建一个名为 的命名事件Process0
。如果成功,则继续。如果失败,它会尝试创建一个命名事件Process1
,然后Process2
等等。您最终会为每个启动的进程创建一个命名事件。有一个EventWaitHandle
构造函数会告诉您句柄是否是新创建的。所以代码看起来像:
List<EventWaitHandle> WaitHandles = new List<EventWaitHandle>();
// at program startup, try to create Process0, with an initial value of set
bool createdNew;
EventWaitHandle wh = new EventWaitHandle(true, EventResetMode.ManualReset, "Process0", out createdNew);
WaitHandles.Add(wh);
int procNum = 1;
while (!createdNew)
{
// Create wait handles with increasing process numbers until one is created new
string whName = "Process" + procNum.ToString();
wh = new EventWaitHandle(false, EventResetMode.ManualReset, whName, out createdNew);
WaitHandles.Add(wh);
++procNum;
}
一个进程现在知道它需要等待哪个等待句柄——WaitHandles
列表中的最后一个。因此,当它完成“咀嚼”阶段时,它可以等待那个句柄:
WaitHandles[WaitHandles.Count - 1].WaitOne();
// print
// Now notify the next wait handle, but only if it exists.
try
{
string whName = "Process" + WaitHandles.Count.ToString();
WaitHandle wh = EventWaitHandle.OpenExisting(whName);
wh.Set();
wh.Dispose();
}
catch (WaitHandleCannotBeOpenedException)
{
// wait handle doesn't exist
}
您可能希望确保在程序退出之前调用列表Dispose
中的每个项目WaitHandles
,尽管如果您忘记了 Windows 会清理您的引用。
如果您没有太多的进程,这应该可以工作。只要这些进程中的任何一个正在运行,“Process0”和直到最后一个的所有其他命名等待句柄都将保留在系统中。因此,如果“Process0”在“Process9”开始之前完成打印,则不会出现作业打印乱序或永远等待通知的问题。
这种方法还可以编写一个可以启动并获取所有句柄及其信号状态的应用程序。如果先前的进程之一在没有发出信号的情况下崩溃,则可以使用这样的应用程序向下一个进程发出信号。
后来补充:
我突然想到这里可能存在竞争条件。假设有三个等待句柄(“Process0”、“Process1”和“Process2”)。Process2 完成打印并尝试通知不存在的 Process3。然后 Process3 在 Process2 退出之前启动,这意味着 Process3 将获得一个名为“Process3”的等待句柄,该句柄永远不会发出信号。
解决这个问题的方法是让 Process0 分配它自己的等待句柄以及 Process1 的等待句柄——也就是说,它在完成打印时必须通知的等待句柄。所有其他进程都做同样的事情。他们没有分配自己的等待句柄(Process0 除外,它分配自己的等待句柄和下一个等待句柄),而是为下一个进程分配等待句柄。这样做应该可以消除竞争条件。