0

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

    myapp.exe 被触发(过程 1) - 开始咀嚼数据(大型数据集)  
    myapp.exe 再次触发(过程 2) - 咀嚼数据  
      进程 2 完成数据分析(小报告) - 将报告发送到打印机  
    myapp.exe 第三次触发(过程 3) - 咀嚼其数据  
      过程 3 完成(也很小) - 将报告发送到打印机  
      过程 1 终于完成了(懒鬼!) - 将报告发送到打印机

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

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

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

4

1 回答 1

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 除外,它分配自己的等待句柄和下一个等待句柄),而是为下一个进程分配等待句柄。这样做应该可以消除竞争条件。

于 2011-03-01T21:23:56.763 回答