В c # .Net 3.5, как я могу управлять несколькими WaitHandles? - PullRequest
3 голосов
/ 19 мая 2011

У меня есть следующий сценарий:

Мое приложение должно импортировать несколько наборов данных из другого приложения, и время имеет решающее значение.
Из-за этого он порождает один поток для каждого импорта.
Итак, скажем, у меня есть импорт от 1 до 10, где импорт 4 и 5 может выполняться только после импорта 1, 6 и 7 после импорта 2 и 8, 9 и 10 после импорта 3

  • Импорт 1
    • Импорт 4
    • Импорт 5
  • Импорт 2
    • Импорт 6
    • Импорт 7
  • Импорт 3
    • Импорт 8
    • Импорт 9
    • Импорт 10

Обыскивая stackoverflow, я нашел несколько ответов о ручках ожидания, но я не уверен, что могу управлять несколькими ручками ожидания.
Я подумал о создании списка импортов / дескрипторов и проверке их в цикле, но я не знаю, как запустить .set () оттуда.
Или создание потока для каждого импорта отца и создание экземпляра дескриптора внутри него, и этот поток отца запускает поток для каждого дочернего импорта, но обработка потоков - это то, что я не уверен, что это правильно.

Любые идеи о том, как я могу решить это?

UPDATE:
После ответа Брайана Гедеона вот что я придумал:
dateSince = Convert.ToDateTime(txtBoxDateSince.Text); dateTo = Convert.ToDateTime(txtBoxDateTo.Text);</p> <p>//Loop all the days on the time interval while (DateTime.Compare(dateSince, dateTo) <= 0) {</p> <pre><code>foreach (ListItem father in checkBoxListFather.Items) { if (father.Selected == true) { processClass process = new processClass(); // This WaitHandle will be used to get the child tasks going. var wh = new ManualResetEvent(false); //Method to Import, wraped in a delegate WaitCallback fatherMethod = new WaitCallback(process.importProcess); //and its parameters processClass.importParameters param = new processClass.importParameters(wh, father.Value, null, dateSince); // Queue the parent task. ThreadPool.QueueUserWorkItem(fundMethod, param); // Register the child tasks. foreach (ListItem child in checkBoxListChild.Items) { if (child.Selected == true) { processClass.importParameters parameters = new processClass.importParameters(null, child.Value, null, dateSince); // Registers a callback for the child task that will execute once the // parent task is complete. WaitOrTimerCallback childMethod = new WaitOrTimerCallback(process.anotherImportProcess); RegisteredWaitHandle rwh = ThreadPool.RegisterWaitForSingleObject(wh, childMethod, parameters, Timeout.Infinite, true); }//End if (child.Selected == true) }//End foreach (ListItem fund in checkBoxListChild.Items) }//End if (father.Selected == true) }//End foreach (ListItem fundProcess in checkBoxListFather.Items) dateSince = dtSince.AddDays(1);

} // Конец while (DateTime.Compare (с тех пор, до) <0) </code>

Практически тот же ответ, просто использовали методы без лямбда-выражений и использовали параметры для них.
Я все еще не проводил стресс-тест, но он работает довольно хорошо

Спасибо, Брайан.

Ответы [ 2 ]

2 голосов
/ 19 мая 2011

Знаете ли вы

  1. Метод WaitHandle.WaitAny
  2. Метод WaitHandle.WaitAll

Если потоки имеют простой линейный характер и вам не требуется очередь для масштабируемости, вы можете использовать C #: ожидание завершения всех потоков

1 голос
/ 19 мая 2011

Если вы действительно хотите выделить один поток для каждой задачи, тогда нет необходимости вообще использовать WaitHandle экземпляров. 1 Вы можете передать экземпляр Thread, который выполняет родительскую задачу, к каждой дочерней задаче и вызовите Join, чтобы убедиться, что родительская задача завершена.

void TaskEntryPoint(Thread parent, ImportTask task)
{
  if (parent != null)
  {
    parent.Join(); // Wait for the parent task to complete.
  }
  task.Execute(); // Execute the child task.
}

Теперь все, что вам нужно сделать, это запустить родительские задачи в их собственных отдельных потоках, а затем получить дочернюю.Задачи собираются своими собственными отдельными потоками.При вызове TaskEntryPoint передайте null, если это для родительской задачи, и передайте соответствующий экземпляр Thread для каждой дочерней задачи.

Обновление:

На основеВ вашем комментарии приведен пример того, как я могу подойти к проблеме, используя ThreadPool.Это довольно сложный шаблон, использующий метод ThreadPool.RegisterWaitForSingleObject.Это также оказывается чрезвычайно масштабируемым решением, поскольку оно использует абсолютный минимум ресурсов для ожидания сигнала WaitHandle.

foreach (ImportTask parent in parentTasks)
{
    // This WaitHandle will be used to get the child tasks going.
    var wh = new ManualResetEvent(false);

    // Needed to capture the loop variable correctly.
    var p = parent; 

    // Queue the parent task.
    ThreadPool.QueueUserWorkItem(
        (state) =>
        {
            try
            {
                // Execute the parent task.
                p.Execute();
            }
            finally
            {
                // Signal the event so that the child tasks can begin executing.
                wh.Set();
            }
        }, null);

    // Register the child tasks.
    foreach (ImportTask child in parent.ChildTasks)
    {
        // Needed to capture the loop variable correctly.
        var c = child;

        // Registers a callback for the child task that will execute once the
        // parent task is complete.
        RegisteredWaitHandle rwh = ThreadPool.RegisterWaitForSingleObject(wh,
            (state, to) =>
            {
                // Execute the child task.
                c.Execute();
            }, 
            null, Timeout.Infinite, true);
    }
}

Волшебство здесь в том, как RegisterWaitForSingleObject ожидаетсобытие.Он зарегистрирует обратный вызов, который будет автоматически выполнен, как только событие будет сообщено.Но он делает это таким образом, что ни один поток в пуле не тратится впустую на ожидание этого события.Опять же, это довольно сложный шаблон, который потребует некоторых размышлений с вашей стороны, но он будет очень масштабируемым.


1 Запуск нового потока длякаждая задача не может быть лучшей стратегией.Рассматривали ли вы использование ThreadPool или Task классов?

...