BackgroundWorker против фоновой темы


166

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

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

Ответы:


88

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

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

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

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


Еще одна проблема, в которой я не уверен, - предположим, я пытаюсь избавиться от формы, на которой работает фоновый работник. Я посылаю сигнал на завершение работы (ManualResetEvent) и через некоторое время DoWork изящно завершает работу. Должен ли я просто позволить форме продолжить и избавиться, хотя DoWork может занять немного больше времени, чтобы завершить работу, или есть какой-то способ (и лучше) поточить. Присоединяйтесь к фоновому рабочему, пока он действительно не завершится, и затем разрешите удаление формы продолжаются?
Фредди Смит

Я думаю, что BackgroundWorker.IsBusy это то, что вы ищете там.
ParmesanCodice

1
Используйте только CancelAsync(и проверьте, будет CancellationPendingли ваш поток опрашиваться с короткими интервалами, если вы хотите, чтобы вместо него было возбуждено исключение, используйте a, System.Threading.Thread.Abort()которое вызывает исключение в самом блоке потока, выберите правильную модель для ситуации.
Бретт Райан,

369

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

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

10
Фоновый рабочий находится в сборке System.dll и пространстве имен System.ComponentModel. Нет зависимости от Winforms.
Кугель

17
Это правильно, но BackgroundWorkerпредназначено для сообщения о продвижении потока заинтересованной стороне, которая обычно включает в себя пользовательский интерфейс. Документация MSDN для класса делает это совершенно ясно. Если вам просто нужно выполнить задачу в фоновом режиме, предпочтите использование ThreadPoolпотока.
Мэтт Дэвис

5
Что касается вашей точки зрения о System.Windows.Formsсборке; BackgroundWorkerтакже полезно для приложений WPF, и эти приложения могут не иметь ссылки на WinForms.
GiddyUpHorsey

12

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

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

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


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

11

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


1
Я думаю, что это хороший момент. Таким образом, сообщение, которое я извлекаю из этого, - это использование фонового работника, если вам нужен фоновый поток «временно» во время жизни формы, однако, если вам нужен фоновый поток для всего времени жизни формы (которое может быть минутами, часами, дней ...) затем используйте Thread вместо BackgroundWorker, чтобы не злоупотреблять назначением ThreadPool
Фредди Смит

Относительно: «... что может вызывать беспокойство, поскольку их только конечное число», вы имеете в виду, что другие приложения в ОС могут нуждаться в них и совместно использовать из того же «пула»?
Дэн W

8

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

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

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

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

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

Параллельное программирование в .NET Framework Managed Threading Best Practices


5

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

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

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


2

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

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


2

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


2

Я хочу указать на одно поведение класса 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 = true; // <--- This will make the thread run in background
    thread.IsBackground = false; // <--- This will delay program termination

    thread.Start();
}

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

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

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


1

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

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


-1

Что меня удивляет, так это то, что дизайнер визуальных студий позволяет вам использовать только фоновые рисунки и таймеры, которые на самом деле не работают с сервисным проектом.

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

Сервисы: использовать только System.Timers.Timer System.Windows.Forms.Timer не будет работать, даже если он доступен в наборе инструментов

Службы: BackgroundWorkers не будут работать, когда он работает как служба. Вместо этого используйте System.Threading.ThreadPools или вызовы Async.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.