.net-IPC-“;“排队”;最古老的流程&x27;先开火

.net-IPC-“;“排队”;最古老的流程&x27;先开火,.net,ipc,waithandle,.net,Ipc,Waithandle,我有一个.NET2.0应用程序,它处理数据,生成Crystal报告,然后将渲染输出发送到打印机。大多数情况下,此应用程序是从Win32应用程序多次启动的。将渲染报告实际发送到打印机所需的时间取决于数据的大小和/或Crystal报告的复杂性。 我的困惑是,无论报表是否准备好打印,我都需要让打印作业按照与流程相同的顺序排队。例如: myapp.exe fired (process 1) - begins chewing on data (large dataset) myapp.exe

我有一个.NET2.0应用程序,它处理数据,生成Crystal报告,然后将渲染输出发送到打印机。大多数情况下,此应用程序是从Win32应用程序多次启动的。将渲染报告实际发送到打印机所需的时间取决于数据的大小和/或Crystal报告的复杂性。
我的困惑是,无论报表是否准备好打印,我都需要让打印作业按照与流程相同的顺序排队。例如:

myapp.exe fired (process 1) - begins chewing on data (large dataset) myapp.exe fired again (process 2) - chews on data process 2 done chewing on data (small report) - sends report to printer myapp.exe fired a third time (process 3) - chews on its data process 3 done (also small) - sends report to printer process 1 is finally done (slacker!) - sends report to printer 已启动myapp.exe(进程1)-开始咀嚼数据(大型数据集) myapp.exe再次被激发(进程2)-啃咬数据 过程2完成数据(小报告)-将报告发送到打印机 myapp.exe第三次启动(进程3)-对其数据进行咀嚼 过程3完成(也很小)-将报告发送到打印机 流程1最终完成(slacker!)-将报告发送到打印机 在上面的示例中,我需要先打印流程1,然后打印流程2,然后打印流程3。然而,只有报告的打印需要按顺序进行-所有的“咀嚼”可以同时进行

我一直在玩弄互斥和信号量,在一个测试应用程序中,第一个进程将首先“打印”,但第二个和第三个(第四个、第五个等)将根据“WaitOne”的发布时间“打印”

我走错路线了吗?

我想我需要的是某种机制,比如IPC的“排队等待句柄”

如果进程数量相对较少,则可以为每个进程创建一个命名的,并且创建等待句柄的顺序将定义允许打印进程的顺序。(请参阅下面的“稍后添加”部分,该部分描述了当前代码中的竞争条件,并讨论了修复方法。)

当进程启动时,它会尝试创建名为
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
列表中的每个项目调用
Dispose
,不过如果您忘记了,Windows会清理您的引用

如果你没有太多的进程,这应该是可行的。只要这些进程中的任何一个正在运行,“Process0”和直到最后一个的所有其他命名等待句柄都将保留在系统中。因此,如果“Process0”在“Process9”开始之前完成打印,则不会出现打印顺序错误或处理作业永远等待通知的问题

此方法还可以编写一个应用程序,该应用程序可以启动并获取所有句柄及其信号状态。如果前一个进程中的一个进程在没有信号的情况下崩溃,则可以使用这样的应用程序向下一个进程发出信号

稍后添加:

我突然想到这里可能有一个竞赛条件。假设有三个等待句柄(“Process0”、“Process1”和“Process2”)。Process2完成其打印并尝试通知不存在的Process3。然后Process3在Process2退出之前启动,这意味着Process3将获得一个名为“Process3”的等待句柄,该句柄永远不会被通知


解决这个问题的方法是让Process0为Process1分配它自己的等待句柄和一个等待句柄——也就是说,当它完成打印时必须通知的等待句柄。所有其他进程都做同样的事情。他们没有分配自己的等待句柄(Process0除外,它分配自己的等待句柄和下一个等待句柄),而是为下一个进程分配等待句柄。这样做应该可以消除比赛条件。

吉姆,这是一个很好的方法。非常感谢。然而,您注意到“如果您没有太多的进程运行,这应该是可行的。”我猜您在谈论性能??另外,我认为您的代码片段中缺少了一个procNum++:D@F3:谢谢。我已经更正了代码。性能是一个问题,是的,尽管我认为几十个等待句柄不会成为问题。如果是几百,我可能会担心。您可能遇到的唯一运行时性能损失是在启动时创建等待句柄。同样,如果只有少数进程的启动时间错开了几秒钟,那也不会有任何问题。@F3:在我编辑的消息末尾,还可以看到有关可能的竞争条件的信息。再次感谢,我真的很感激;注意到比赛状态。我会根据你的建议做一些事情,并适当地更新。
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
}