BackgroundWorker против фоновой темы - PullRequest
157 голосов
/ 02 октября 2009

У меня есть стилистический вопрос о выборе реализации фонового потока, который я должен использовать в приложении Windows Form. В настоящее время у меня есть BackgroundWorker в форме, которая имеет бесконечный цикл (while(true)). В этом цикле я использую WaitHandle.WaitAny, чтобы поток дремал, пока не произойдет что-то интересное. Один из дескрипторов события, которого я жду, - это событие "StopThread", чтобы я мог выйти из цикла. Это событие сигнализируется, когда из моего переопределенного Form.Dispose().

Я где-то читал, что BackgroundWorker действительно предназначен для операций, с которыми вы не хотите связывать пользовательский интерфейс и которые имеют конечный конец - например, загрузка файла или обработка последовательности элементов. В этом случае «конец» неизвестен и только тогда, когда окно закрыто. Поэтому для меня было бы более уместным использовать фоновый поток вместо BackgroundWorker для этой цели?

Ответы [ 11 ]

352 голосов
/ 02 октября 2009

Некоторые мои мысли ...

  1. Используйте BackgroundWorker , если у вас есть одна задача, которая выполняется в фоновом режиме и должна взаимодействовать с пользовательским интерфейсом. Задача распределения данных и вызовов методов в потоке пользовательского интерфейса обрабатывается автоматически с помощью его модели, основанной на событиях. Избегайте BackgroundWorker, если ...
    • ваша сборка не имеет или не взаимодействует напрямую с пользовательским интерфейсом,
    • вам нужен поток, чтобы быть потоком переднего плана, или
    • вам нужно манипулировать приоритетом потока.
  2. Используйте поток ThreadPool , когда требуется эффективность. ThreadPool помогает избежать накладных расходов, связанных с созданием, запуском и остановкой потоков. Избегайте использования ThreadPool, если ...
    • задача выполняется на весь срок службы вашего приложения,
    • вам нужно, чтобы нить была основной,
    • вам нужно манипулировать приоритетом потока или
    • вам нужно, чтобы поток имел фиксированный идентификатор (прерывание, приостановка, обнаружение).
  3. Используйте класс Thread для длительных задач и когда вам требуются функции, предлагаемые формальной моделью потоков, например, выбор между передним и фоновым потоками, настройка приоритета потока, детальное управление потоком исполнение и т. д.
83 голосов
/ 02 октября 2009

Из моего понимания вашего вопроса вы используете BackgroundWorker в качестве стандартной темы.

Причина, по которой BackgroundWorker рекомендуется для вещей, которые вы не хотите связывать с потоком пользовательского интерфейса, заключается в том, что он предоставляет некоторые приятные события при разработке Win Forms.

События типа RunWorkerCompleted, сигнализирующие, когда поток завершил то, что ему нужно было сделать, и событие ProgressChanged для обновления графического интерфейса пользователя в ходе выполнения потока.

Так что, если вы не используете их, я не вижу никакого вреда в использовании стандартной темы для того, что вам нужно сделать.

12 голосов
/ 23 октября 2009

Почти то, что сказал Мэтт Дэвис, со следующими дополнительными точками:

Для меня главный дифференциатор с BackgroundWorker - это автоматическая сортировка завершенного события через SynchronizationContext. В контексте пользовательского интерфейса это означает, что завершенное событие запускается в потоке пользовательского интерфейса и может использоваться для обновления пользовательского интерфейса. Это главное отличие, если вы используете BackgroundWorker в контексте пользовательского интерфейса.

Задачи, выполняемые с помощью ThreadPool, не могут быть легко отменены (это включает ThreadPool. QueueUserWorkItem и делегаты выполняются асинхронно). Таким образом, несмотря на то, что это позволяет избежать накладных расходов на ускорение потока, если вам нужно отменить, либо используйте BackgroundWorker, либо (что более вероятно вне пользовательского интерфейса) раскрутите поток и сохраните ссылку на него, чтобы вы могли вызвать Abort().

10 голосов
/ 02 октября 2009

Также вы связываете поток пула для времени жизни фонового работника, что может вызывать беспокойство, поскольку их существует только конечное число. Я бы сказал, что если вы когда-либо создаете поток только один раз для своего приложения (и не используете какие-либо функции фонового рабочего), тогда используйте поток, а не поток фонового работника / пула потоков.

8 голосов
/ 21 апреля 2011

Знаете, иногда проще всего работать с BackgroundWorker независимо от того, используете ли вы Windows Forms, WPF или какую-либо другую технологию. Отличная часть этих парней в том, что вы получаете многопоточность, не беспокоясь о том, где исполняется нить, что отлично подходит для простых задач.

Прежде чем использовать BackgroundWorker, сначала подумайте, хотите ли вы отменить поток (закрытие приложения, отмена пользователя), а затем вам нужно решить, должен ли ваш поток проверять отмены или он должен быть наложен на само выполнение.

BackgroundWorker.CancelAsync() установит CancellationPending на true, но больше ничего не будет делать, тогда ответственность за непрерывную проверку потоков будет нести ответственность, также имейте в виду, что в этом подходе вы можете столкнуться с состоянием гонки. Ваш пользователь отменил, но поток завершен до тестирования на CancellationPending.

Thread.Abort(), с другой стороны, вызовет исключение в выполнении потока, которое вызывает отмену этого потока, однако вы должны быть осторожны с тем, что может быть опасно, если это исключение внезапно возникло в ходе выполнения.

Потоки нуждаются в очень тщательном рассмотрении независимо от задачи, для дальнейшего чтения:

Параллельное программирование в .NET Framework Рекомендации по управляемым потокам

4 голосов
/ 28 октября 2009

Я знал, как использовать потоки, прежде чем я знал .NET, поэтому потребовалось некоторое время, чтобы привыкнуть, когда я начал использовать BackgroundWorkers. Мэтт Дэвис суммировал разницу с большим превосходством, но я хотел бы добавить, что труднее понять, что именно делает код, и это может усложнить отладку. IMO проще думать о создании и закрытии потоков, чем думать о предоставлении работы пулу потоков.

Я до сих пор не могу комментировать посты других людей, поэтому простите мою кратковременную хромоту, используя ответ на адрес 7

Не использовать Thread.Abort (); вместо этого, сообщите о событии и сделайте так, чтобы ваша нить изящно заканчивалась при сигнале. Thread.Abort () вызывает исключение ThreadAbortException в произвольной точке выполнения потока, что может привести к несчастным случаям, таким как потерянные мониторы, поврежденное общее состояние и т. http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx

2 голосов
/ 02 октября 2009

Основное отличие, как вы заявили, заключается в создании событий GUI из BackgroundWorker. Если потоку не нужно обновлять отображение или генерировать события для основного потока графического интерфейса, тогда это может быть простой поток.

2 голосов
/ 02 октября 2009

Если это не сломано - починить, пока не станет ... просто шучу:)

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

1 голос
/ 11 июля 2017

Я хочу указать на одно поведение класса BackgroundWorker, которое еще не упоминалось. Вы можете создать обычный поток для работы в фоновом режиме, установив свойство Thread.IsBackground.

Фоновые потоки идентичны приоритетным, за исключением того, что фоновые потоки не препятствуют завершению процесса. [ 1 ]

Вы можете проверить это поведение, вызвав следующий метод в конструкторе окна формы.

void TestBackgroundThread()
{
    var thread = new Thread((ThreadStart)delegate()
    {
        long count = 0;
        while (true)
        {
            count++;
            Debug.WriteLine("Thread loop count: " + count);
        }
    });

    // Choose one option:
    thread.IsBackground = false; // <--- This will make the thread run in background
    thread.IsBackground = true; // <--- This will delay program termination

    thread.Start();
}

Если для свойства IsBackground установлено значение true и вы закрываете окно, то ваше приложение будет нормально завершено.

Но если для свойства IsBackground установлено значение false (по умолчанию) и вы закрываете окно, то только окно исчезнет, ​​но процесс все равно продолжит работать.

Класс BackgroundWorker использует Thread, который работает в фоновом режиме.

1 голос
/ 02 октября 2009

Фоновый рабочий - это класс, который работает в отдельном потоке, но предоставляет дополнительные функциональные возможности, которые вы не получаете с простым потоком (например, обработка отчета о ходе выполнения задачи).

Если вам не нужны дополнительные функции, предоставляемые фоновым работником - и, похоже, вам это не нужно - тогда поток будет более подходящим.

...