.net - IPC - «очередь» самого старого процесса, «запускаемого первым» - PullRequest
0 голосов
/ 01 марта 2011

У меня есть приложение .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. Однако только печать отчета должна происходить последовательно - все «жевательные» могут быть сделано одновременно ...

Я играл с Mutex и Semaphore, и дошел до точки в тестовом приложении, где первый процесс будет «печатать» первый, а второй и третий (четвертый, пятый и т. Д.) - «» печать "в зависимости от того, когда их" WaitOne "был выпущен.

Я иду по неправильному маршруту здесь?
Я думаю, что мне нужен какой-то механизм, такой как «Queued WaitHandle» для IPC ...

1 Ответ

0 голосов
/ 02 марта 2011

Если число процессов относительно невелико, вы можете создать для каждого из них имя 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, который выделяет свой собственный дескриптор ожидания И следующий), они выделяют дескриптор ожидания для следующего процесса.Такое действие должно устранить состояние гонки.

...