BackgroundWorker против фоновой темы
У меня есть стилистический вопрос о выборе реализации фонового потока, который я должен использовать в приложении Windows Form. В настоящее время у меня есть BackgroundWorker
в форме, которая имеет бесконечное (while(true))
петля. В этом цикле я использую WaitHandle.WaitAny
держать нить дремлет, пока не произойдет что-то интересное. Один из дескрипторов события, на котором я жду, это "StopThread
"событие, чтобы я мог выйти из цикла. Это событие сигнализируется, когда из моего переопределенного Form.Dispose()
,
Я где-то читал, что BackgroundWorker
действительно предназначен для операций, с которыми вы не хотите связывать пользовательский интерфейс и имеющих конечный конец - например, загрузка файла или обработка последовательности элементов. В этом случае "конец" неизвестен и только тогда, когда окно закрыто. Поэтому для меня было бы более уместным использовать фоновый поток вместо BackgroundWorker
для этого?
12 ответов
Исходя из моего понимания вашего вопроса, вы используете BackgroundWorker
как стандартная тема.
Причина по которой BackgroundWorker
рекомендуется для вещей, которые вы не хотите связывать с потоком пользовательского интерфейса, потому что он предоставляет некоторые приятные события при разработке Win Forms.
События как RunWorkerCompleted
сигнализировать, когда поток завершил то, что ему нужно было сделать, и ProgressChanged
событие для обновления графического интерфейса на продвижении потоков.
Поэтому, если вы ими не пользуетесь, я не вижу никакого вреда в использовании стандартной темы для того, что вам нужно сделать.
Некоторые из моих мыслей...
- Используйте BackgroundWorker, если у вас есть одна задача, которая работает в фоновом режиме и должна взаимодействовать с пользовательским интерфейсом. Задача распределения данных и вызовов методов в потоке пользовательского интерфейса обрабатывается автоматически с помощью его модели, основанной на событиях. Избегайте BackgroundWorker, если...
- ваша сборка не имеет или не взаимодействует напрямую с пользовательским интерфейсом,
- вам нужен поток, чтобы быть потоком переднего плана, или
- вам нужно манипулировать приоритетом потока.
- Используйте поток ThreadPool, когда эффективность требуется. ThreadPool помогает избежать накладных расходов, связанных с созданием, запуском и остановкой потоков. Избегайте использования ThreadPool, если...
- задача выполняется в течение всего срока действия вашего приложения,
- вам нужна нить, чтобы быть нитью переднего плана,
- вам нужно манипулировать приоритетом потока, или
- вам нужно, чтобы поток имел фиксированную идентификацию (прерывание, приостановка, обнаружение).
- Используйте класс Thread для длительных задач и когда вам требуются функции, предлагаемые формальной моделью потоков, например, выбор между передним и фоновым потоками, настройка приоритета потока, детальный контроль над выполнением потока и т. Д.
Почти то, что сказал Мэтт Дэвис, со следующими дополнительными пунктами:
Для меня главный дифференциатор с BackgroundWorker
это автоматическая сортировка завершенного события через SynchronizationContext
, В контексте пользовательского интерфейса это означает, что завершенное событие запускается в потоке пользовательского интерфейса и может использоваться для обновления пользовательского интерфейса. Это главное отличие, если вы используете BackgroundWorker
в контексте пользовательского интерфейса.
Задачи, выполняемые через ThreadPool
не может быть легко отменено (это включает в себя ThreadPool
, QueueUserWorkItem
и делегаты выполняют асинхронно). Так что, хотя это позволяет избежать накладных расходов на раскручивание потоков, если вам нужно отменить, либо используйте BackgroundWorker
или (более вероятно, за пределами пользовательского интерфейса) раскрутить поток и сохранить ссылку на него, чтобы вы могли вызвать Abort()
,
Также вы связываете поток пула для времени жизни фонового работника, что может вызывать беспокойство, поскольку их существует только конечное число. Я бы сказал, что если вы когда-либо создаете поток только один раз для своего приложения (и не используете какие-либо функции фонового рабочего), то используйте поток, а не поток фонового работника / пула потоков.
Знаете, иногда проще всего работать с BackgroundWorker независимо от того, используете ли вы Windows Forms, WPF или какую-либо другую технологию. Отличная часть этих парней в том, что вы получаете многопоточность, не беспокоясь о том, где выполняется ваш поток, что отлично подходит для простых задач.
Перед использованием BackgroundWorker
сначала подумайте, хотите ли вы отменить поток (закрытие приложения, отмена пользователя), а затем вам нужно решить, должен ли ваш поток проверять отмены или он должен быть наложен на само выполнение.
BackgroundWorker.CancelAsync()
установит CancellationPending
в true
но больше ничего делать не будет, тогда ответственность за постоянную проверку потоков лежит на потоках, имейте в виду, что в этом подходе вы можете столкнуться с условием гонки, когда ваш пользователь отменил, но поток завершился до тестирования на CancellationPending
,
Thread.Abort()
с другой стороны, при выполнении потока будет сгенерировано исключение, которое принудительно отменяет этот поток, однако вы должны быть осторожны с тем, что может быть опасно, если это исключение внезапно возникло в ходе выполнения.
Потоки нуждаются в очень тщательном рассмотрении независимо от того, что задача, для дальнейшего чтения:
Рекомендации по параллельному программированию в.NET Framework.
Я знал, как использовать потоки, прежде чем я знал.NET, поэтому потребовалось некоторое привыкание, когда я начал использовать BackgroundWorkers. Мэтт Дэвис суммировал разницу с большим превосходством, но я хотел бы добавить, что труднее понять, что именно делает код, и это может усложнить отладку. IMO проще думать о создании и закрытии потоков, чем думать о том, чтобы дать работу пулу потоков.
Я до сих пор не могу комментировать посты других людей, поэтому простите мою кратковременную хромоту, используя ответ на адрес 7
Не используйте Thread.Abort(); вместо этого, сообщите о событии и сделайте так, чтобы ваша нить изящно заканчивалась при сигнале. Thread.Abort() вызывает исключение ThreadAbortException в произвольной точке выполнения потока, что может привести к несчастным случаям, таким как потерянные мониторы, поврежденное общее состояние и т. Д. http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx
Я хочу указать на одно поведение класса 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 использует поток, который работает в фоновом режиме.
Если это не сломано - исправьте это, пока это... просто шучу:)
А если серьезно, то BackgroundWorker, вероятно, очень похож на то, что у вас уже есть, если бы вы начали с него с самого начала, возможно, вы бы сэкономили некоторое время - но на данный момент я не вижу в этом необходимости. Если что-то не работает или вы думаете, что ваш текущий код трудно понять, я бы придерживался того, что у вас есть.
Основное отличие состоит в том, что, как вы заявили, генерация событий GUI из BackgroundWorker
, Если потоку не нужно обновлять отображение или генерировать события для основного потока графического интерфейса, то это может быть простой поток.
Фоновый рабочий - это класс, который работает в отдельном потоке, но он предоставляет дополнительные функциональные возможности, которые вы не получаете с простым потоком (например, обработка отчета о ходе выполнения задачи).
Если вам не нужны дополнительные функции, предоставляемые фоновым работником - и, похоже, вам это не нужно - тогда поток будет более подходящим.
если вы генерируете исключение внутри потока, это приведет к закрытию приложения WinForms. Исключения, сгенерированные BackgroundWorker внутри, не повлияют на работающее приложение.
Что меня смущает, так это то, что дизайнер визуальных студий позволяет вам использовать только фоновые рисунки и таймеры, которые на самом деле не работают с сервисным проектом.
Это дает вам аккуратные элементы управления перетаскиванием на ваш сервис, но... даже не пытайтесь развернуть его. Не сработает
Службы: используйте только System.Timers.Timer. System.Windows.Forms.Timer не будет работать, даже если он доступен на панели инструментов.
Службы: BackgroundWorkers не будут работать, когда он работает как служба. Вместо этого используйте System.Threading.ThreadPools или вызовы Async.