.net - IPC - “队列”最古老的进程工作先开火

发布于 2024-10-19 23:03:39 字数 889 浏览 0 评论 0原文

我有一个 .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 打印第 1 个,然后是进程 2,然后是进程 3。但是,需要按顺序打印报告 - 所有“咀嚼”可以同时完成......

我一直在玩弄互斥体和信号量,并且我已经在测试应用程序中达到了这样的程度,其中第一个进程将首先“打印”,但第二个和第三个(第四,第五等)将根据“WaitOne”发出的时间“打印”。

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

I have a .Net 2.0 application that processes data, generates Crystal Reports, and then sends the rendered output to a printer. This application is most-of-the-time fired from a Win32 application multiple times. The time it takes to actually send the rendered report to the printer depends on the size of the data and/or the complexity of the Crystal Report.
My quandary is that I need to have the print jobs queued in the same order as the the process, regardless of whether or not the report is ready to be printed. So, for instance:

    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

In the above example, I need process 1 to print 1st, then process 2, then process 3. However, only the printing of the report needs to happen in sequence - all the "chewing" can be done simultaneously...

I've been toying with Mutex and Semaphore, and I've got it to a point in a test app where the first process will "print" first, but the second and third (fourth, fifth, etc) will "print" depending on when their "WaitOne" was issued.

Am I going the wrong route here?
I think what i need is some kind of mechanism like a "Queued WaitHandle" for IPC...

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

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

发布评论

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

评论(1

会傲 2024-10-26 23:03:39

如果进程数量相对较少,您可以创建一个名为 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
}

您可能需要确保对 WaitHandles 列表中的每个项目调用 Dispose在程序退出之前,尽管 Windows 会在您忘记时清除您的引用。

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

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

稍后添加:

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

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

If the number of processes is relatively small, you could create a named EventWaitHandle for each one, and the order in which the wait handles are created would define the order in which processes are allowed to print. (See the "Added later" section below, which describes a race condition in the presented code and discusses a fix.)

When a process starts up, it tries to create a named event with the name Process0. If it succeeds, it goes on. If it fails, it tries to create a named event Process1, then Process2, etc. You end up with one named event for every process that starts up. There is an EventWaitHandle constructor that will tell you if the handle was created new. So the code looks something like:

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;
}

A process now knows which wait handle it needs to wait on--the last one in the WaitHandles list. So when it's done with its "chewing" phase, it can wait on that handle:

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
}

You'll probably want to be sure to call Dispose on each item in the WaitHandles list before your program exits, although Windows will clean up your references if you forget.

This should work if you don't have too many processes going. As long as any one of these processes is running, the "Process0" and all other named wait handles up to the last one will remain in the system. So if "Process0" finishes printing before "Process9" even starts, there won't be a problem with a job printing out of order or handing forever waiting for notification.

This method also makes it possible to write an application that can start up and get all of the handles along with their signaled state. Such an application could be used to signal the next process in line if one of the previous processes crashed without signaling.

Added later:

It occurs to me that there's a possible race condition here. Say there are three wait handles ("Process0", "Process1", and "Process2"). Process2 finishes its printing and attempts to notify Process3, which doesn't exist. Then Process3 starts up before Process2 exits, meaning that Process3 will get a wait handle named "Process3" that will never be signaled.

The way around this would be to have Process0 allocate its own wait handle and also a wait handle for Process1 -- that is, the wait handle that it will have to notify when it's done printing. All other processes do the same thing. Rather than allocating their own wait handle (except for Process0, which allocates its own wait handle AND the next one), they allocate the wait handle for the next process. Doing it this way should eliminate the race condition.

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