Координирующее завершение работы рабочего потока

#c# #vb.net #multithreading

#c# #vb.net #многопоточность

Вопрос:

У меня есть a List(Of IWorker) , который содержит несколько конкретных данных. IWorker в свою очередь, имеет Startup() Shutdown() метод and .

Внутри рабочего класса Startup() создается новый рабочий поток, затем возвращается. Shutdown() в настоящее время устанавливает частный ShutDownRequested флаг и возвращается. Флаг проверяется рабочим потоком (в худшем случае каждые несколько секунд).

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

Я вижу несколько способов обойти это, но не уверен, какой из них лучший.

  • Добавьте State свойство в IWorker . Должно сработать, но я собираюсь опрашивать состояния, которые кажутся немного неприятными.
  • Сделайте блокировку команды выключения — вероятно, это было бы моим идеальным решением, но я не уверен, как это реализовать — является ли рабочий Sunclock частным объектом, который Shutdown() также должен пытаться синхронизироваться после установки флага?
  • Что-то еще?

Любые статьи, советы или предложения приветствуются. Примеры на C # или VB подойдут.

Комментарии:

1. Это зависит от того, что вы хотите сделать и почему. Вы хотите блокировать при вызове Shutdown() ? Вы хотите показать статус пользователю? Что-то еще?

2. @svick Это выполняется в консольном приложении. В настоящее время после вызова запуска рабочих консоль просто выполняет цикл каждые несколько секунд, считывает некоторую информацию о состоянии от рабочих и отображает ее (обработанные события, используемая пропускная способность и т. Д.). Когда пользователь нажимает Enter для выхода из приложения, завершение работы отправляется каждому работнику. Я просто хочу дождаться завершения завершения работы, распечатать подтверждение чистого завершения работы и затем выйти. Если приложение не завершает работу и рабочий завершает работу, оно перерабатывается и запускается снова.

Ответ №1:

Я бы не стал пытаться использовать блокировку для блокировки Shutdown() , если вы хотите, чтобы завершение работы было операцией блокировки.

Если вы можете использовать .NET 4, я бы сделал ваш внутренний метод выполнения задачей. Затем вы можете просто вызвать Task.Wait() блокировку, пока она не будет завершена.

Если вы должны использовать .NET 3.5 или более раннюю версию, я бы Shutdown() попросил внутренний вызов wait для WaitHandle, например a ManualResetEvent . Код завершения работы может выглядеть следующим образом:

 ' Include:
Private shutdownEvent as ManualResetEvent = new ManualResetEvent(false)

Sub Shutdown()
    ShutDownRequested = True
    shutdownEvent.WaitOne()
End Sub
 

Затем цикл опроса (операция потока) будет выполнять:

 Sub Execute()
    While Not ShutDownRequested
        ' Do your work
    End While

    ' Before you exit, "set" the event:
    shutdownEvent.Set()
End Sub
 

Комментарии:

1. И используя задачи, вы также можете легко дождаться их всех сразу, используя Task.WaitAll() .

2. Спасибо за подробный ответ. В настоящее время я застрял в 3.5 для этого проекта. Не могли бы вы объяснить, какие преимущества у вас ManualResetEvent есть по сравнению с использованием Thread.Join , как предложено в другом ответе?

3. @Basiclife Основными преимуществами являются: 1) Вы можете (необязательно) установить тайм-аут для WaitOne(), что позволит вам возвращаться, а не блокировать навсегда в случае плохо написанного IWorker , 2) вам не нужно сохранять ссылку на поток, что позволяет ему бытьGC’ed по завершении. 3) Очень четко указано, что происходит.

4. @Reed спасибо за объяснение — все очень хорошие моменты, я буду использовать это в будущем.

Ответ №2:

Существует метод Thread .Соединение, которое будет блокироваться до тех пор, пока поток не закончится — какая-либо польза?

Ответ №3:

Почему бы не добавить ShutdownComplete событие IWorker и не убедиться, что это последнее, что делает каждый конкретный, когда он завершен? Я знаю, это звучит так, как будто это может заставить реализации делать что-то конкретное, но если у вас есть базовый класс для IWorker, это должно иметь большое значение. Подумайте об этом.

 public abstract class WorkerBase : IWorker
{
  public event EventHandler ShutdownComplete;
  //...other implemenation details

  public void Shutdown()
  {
    this.ShutdownInternal();
    this.ShutdownComplete(this, EventArgs.Empty);
  }

  protected abstract void ShutdownInternal();

}