.net - IPC - “排队”最早的进程'工作首先开火

时间:2011-03-01 20:14:43

标签: .net ipc waithandle

我有一个.Net 2.0应用程序,它处理数据,生成Crystal Reports,然后将渲染的输出发送到打印机。此应用程序是Win32应用程序多次触发的大部分时间。将呈现的报告实际发送到打印机所需的时间取决于数据的大小和/或Crystal Report的复杂程度。
我的窘境是,我需要按照与流程相同的顺序排列打印作业,无论报告是否准备好打印。所以,例如:

    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

在上面的例子中,我需要进程1来打印1st,然后是进程2,然后是进程3.但是,报告的打印需要按顺序进行 - 所有“咀嚼”可以同时完成......

我一直在玩Mutex和Semaphore,我已经在测试应用程序中得到了一点,第一个进程将首先“打印”,但第二个和第三个进程(第四个,第五个等)将“打印“取决于何时发出”WaitOne“。

我在这里走错路吗? 我认为我需要的是某种机制,比如IPC的“Queued WaitHandle”......

1 个答案:

答案 0 :(得分:0)

如果进程数相对较少,则可以为每个进程创建一个名为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,它分配自己的等待句柄和下一个句柄),它们为下一个进程分配等待句柄。这样做可以消除竞争条件。