Рассмотрим гипотетический метод объекта, который делает что-то за вас:
public class DoesStuff
{
BackgroundWorker _worker = new BackgroundWorker();
...
public void CancelDoingStuff()
{
_worker.CancelAsync();
//todo: Figure out a way to wait for BackgroundWorker to be cancelled.
}
}
Как можно дождаться завершения BackgroundWorker?
В прошлом люди пробовали:
while (_worker.IsBusy)
{
Sleep(100);
}
Но это взаимоблокировки , потому что IsBusy
не очищается до тех пор, пока RunWorkerCompleted
событие не будет обработано, и это событие не может быть обработано, пока приложение не перейдет в режим ожидания. Приложение не будет бездействовать, пока рабочий не закончит работу. (Плюс, это замкнутый круг - отвратительно.)
Другие добавили, что предлагается включить его в:
while (_worker.IsBusy)
{
Application.DoEvents();
}
Проблема в том, что это Application.DoEvents()
вызывает обработку сообщений, находящихся в настоящее время в очереди, что вызывает проблемы повторного входа (.NET не повторяется).
Я бы надеялся использовать какое-то решение, включающее объекты синхронизации событий, где код ожидает события, которое рабочийRunWorkerCompleted
устанавливает обработчик событий . Что-то вроде:
Event _workerDoneEvent = new WaitHandle();
public void CancelDoingStuff()
{
_worker.CancelAsync();
_workerDoneEvent.WaitOne();
}
private void RunWorkerCompletedEventHandler(sender object, RunWorkerCompletedEventArgs e)
{
_workerDoneEvent.SetEvent();
}
Но я вернулся в тупик: обработчик событий не может работать, пока приложение не перейдет в режим ожидания, и приложение не перейдет в режим ожидания, потому что оно ожидает события.
Итак, как вы можете дождаться завершения BackgroundWorker?
Обновление Люди, кажется, сбиты с толку этим вопросом. Кажется, они думают, что я буду использовать BackgroundWorker как:
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += MyWork;
worker.RunWorkerAsync();
WaitForWorkerToFinish(worker);
Это не то, это не то, что я делаю, и не об этом здесь спрашивают. Если бы это было так, не было бы смысла использовать фонового воркера.
источник
((BackgroundWorker)sender).CancellationPending
чтобы получить событие отменыС этим ответом есть проблема . Пользовательский интерфейс должен продолжать обрабатывать сообщения, пока вы ждете, иначе он не будет перерисовываться, что будет проблемой, если вашему фоновому рабочему потребуется много времени, чтобы ответить на запрос отмены.
Второй недостаток заключается в том, что
_resetEvent.Set()
он никогда не будет вызван, если рабочий поток генерирует исключение, оставляя основной поток ожидающим на неопределенное время, однако этот недостаток можно легко исправить с помощью блока try / finally.Один из способов сделать это - отобразить модальное диалоговое окно с таймером, который неоднократно проверяет, завершил ли фоновый рабочий процесс работу (или завершил ли ее отмену в вашем случае). Как только фоновый исполнитель завершит работу, модальное диалоговое окно вернет управление вашему приложению. Пока это не произойдет, пользователь не сможет взаимодействовать с пользовательским интерфейсом.
Другой метод (при условии, что у вас открыто не более одного немодального окна) - установить ActiveForm.Enabled = false, затем зацикливаться на Application, DoEvents, пока фоновый рабочий не завершит отмену, после чего вы можете снова установить ActiveForm.Enabled = true.
источник
Почти всех вас смущает этот вопрос, и вы не понимаете, как используется воркер.
Рассмотрим обработчик событий RunWorkerComplete:
И все хорошо.
Теперь возникает ситуация, когда вызывающему абоненту необходимо прервать обратный отсчет, потому что ему нужно выполнить аварийное самоуничтожение ракеты.
Также существует ситуация, когда нам нужно открыть ворота доступа к ракете, но не во время обратного отсчета:
И, наконец, нам нужно слить топливо из ракеты, но это не допускается во время обратного отсчета:
Без возможности дождаться отмены рабочего процесса, мы должны переместить все три метода в RunWorkerCompletedEvent:
Теперь я мог бы написать свой код таким образом, но я просто не собираюсь. Мне все равно, просто нет.
источник
Вы можете проверить RunWorkerCompletedEventArgs в RunWorkerCompletedEventHandler, чтобы узнать, каков был статус. Успех, отменен или ошибка.
Обновление : чтобы узнать, вызвал ли ваш работник .CancelAsync (), используя это:
источник
Ты не ждать завершения фонового рабочего. Это в значительной степени противоречит цели запуска отдельного потока. Вместо этого вы должны позволить вашему методу закончить и переместить любой код, который зависит от завершения, в другое место. Вы позволяете рабочему сообщить вам, когда это будет сделано, и затем вызвать оставшийся код.
Если вы хотите дождаться завершения чего-либо, используйте другую конструкцию потоков, которая предоставляет WaitHandle.
источник
Почему нельзя просто привязать событие BackgroundWorker.RunWorkerCompleted? Это обратный вызов, который «Произойдет, когда фоновая операция завершена, была отменена или вызвала исключение».
источник
Я не понимаю, почему вам нужно ждать завершения BackgroundWorker; это действительно похоже на полную противоположность мотивации класса.
Однако вы можете запустить каждый метод с вызова worker.IsBusy и заставить их выйти, если он работает.
источник
Просто хочу сказать, что я пришел сюда, потому что мне нужно, чтобы фоновый рабочий подождал, пока я выполняю асинхронный процесс в цикле, мое исправление было намного проще, чем все остальные вещи ^^
Просто подумал, что поделюсь, потому что именно здесь я оказался в поисках решения. Кроме того, это мой первый пост о переполнении стека, поэтому, если это плохо или что-то еще, я бы хотел критиков! :)
источник
Хм, может, я неправильно понял твой вопрос.
Backgroundworker вызывает событие WorkerCompleted после того, как его «workermethod» (метод / функция / подпрограмма, которая обрабатывает событие backgroundworker.doWork ) завершается, поэтому нет необходимости проверять, работает ли BW. Если вы хотите остановить своего рабочего, проверьте свойство ожидания отмены внутри вашего «рабочего метода».
источник
Рабочий процесс
BackgroundWorker
объекта в основном требует, чтобы вы обрабатывалиRunWorkerCompleted
событие как для нормального выполнения, так и для вариантов использования отмены пользователем. Вот почему свойство RunWorkerCompletedEventArgs.Cancelled существует . По сути, правильное выполнение этого требует, чтобы вы считали ваш метод Cancel асинхронным методом.Вот пример:
Если вы действительно не хотите, чтобы ваш метод завершился, я бы предложил поставить флаг, например,
AutoResetEvent
на производныйBackgroundWorker
, а затем переопределить,OnRunWorkerCompleted
чтобы установить флаг. Хотя это все еще немного неуклюже; Я бы рекомендовал рассматривать событие отмены как асинхронный метод и делать то, что он делает в данный момент, вRunWorkerCompleted
обработчике.источник
Я немного опоздал на вечеринку (около 4 лет), но как насчет настройки асинхронного потока, который может обрабатывать цикл занятости без блокировки пользовательского интерфейса, а затем обратный вызов из этого потока будет подтверждением того, что BackgroundWorker завершил отмену ?
Что-то вроде этого:
По сути, это запускает другой поток для работы в фоновом режиме, который просто ждет в своем цикле занятости, чтобы увидеть
MyWorker
, завершился ли процесс . ПослеMyWorker
завершения отмены поток выйдет, и мы сможем использовать егоAsyncCallback
для выполнения любого метода, который нам понадобится для отслеживания успешной отмены - это будет работать как псевдо-событие. Поскольку он отделен от потока пользовательского интерфейса, он не будет блокировать пользовательский интерфейс, пока мы ждемMyWorker
завершения отмены. Если ваше намерение действительно состоит в том, чтобы заблокировать и дождаться отмены, тогда это бесполезно для вас, но если вы просто хотите подождать, чтобы запустить другой процесс, тогда это прекрасно работает.источник
Я знаю, что это действительно поздно (5 лет), но то, что вы ищете, - это использовать Thread и SynchronizationContext. . Вам придется маршалировать вызовы пользовательского интерфейса обратно в поток пользовательского интерфейса «вручную», а не позволять Framework делать это автоматически.
Это позволяет вам использовать поток, который вы можете ждать, если это необходимо.
источник
источник
Решение этой проблемы Фредриком Калсетом - лучшее, что я нашел до сих пор. Другие решения
Application.DoEvent()
могут вызывать проблемы или просто не работать. Позвольте мне преобразовать его решение в класс многократного использования. ПосколькуBackgroundWorker
он не запечатан, мы можем унаследовать от него наш класс:Используя флаги и правильную блокировку, мы гарантируем, что он
_resetEvent.WaitOne()
действительно будет вызван только в том случае, если какая-то работа была начата, иначе_resetEvent.Set();
может никогда не быть вызвана!Команда try-finally гарантирует, что
_resetEvent.Set();
она будет вызвана, даже если в нашем обработчике DoWork должно произойти исключение. Иначе приложение могло зависнуть при звонке навсегдаCancelSync
!Мы бы использовали это так:
Вы также можете добавить обработчик к
RunWorkerCompleted
событию, как показано здесь:BackgroundWorker Class (документация Microsoft) .
источник
При закрытии формы мой открытый файл журнала закрывается. Мой фоновый рабочий записывает этот файл журнала, поэтому я не могу
MainWin_FormClosing()
завершить его, пока мой фоновый рабочий процесс не завершится. Если я не дождусь завершения работы моего фонового рабочего стола, произойдут исключения.Почему это так сложно?
Простой
Thread.Sleep(1500)
работает, но откладывает выключение (если слишком долго) или вызывает исключения (если слишком короткий).Чтобы завершить работу сразу после завершения фонового рабочего, просто используйте переменную. Это работает для меня:
источник
Вы можете отказаться от события RunWorkerCompleted. Даже если вы уже добавили обработчик событий для _worker, вы можете добавить еще один, и они будут выполняться в том порядке, в котором они были добавлены.
это может быть полезно, если у вас есть несколько причин, по которым может произойти отмена, что делает логику одного обработчика RunWorkerCompleted более сложной, чем вы хотите. Например, отмена, когда пользователь пытается закрыть форму:
источник
Я использую
async
метод иawait
жду, пока рабочий закончит свою работу:и в
DoWork
методе:Вы также можете инкапсулировать
while
петлюDoWork
с ,try ... catch
чтобы установить_isBusy
этоfalse
на исключение. Или просто проверить_worker.IsBusy
вStopAsync
цикле.Вот пример полной реализации:
Чтобы остановить воркер и дождаться его завершения:
Проблемы с этим методом:
источник
о боже, некоторые из них стали до смешного сложными. все, что вам нужно сделать, это проверить свойство BackgroundWorker.CancellationPending внутри обработчика DoWork. Вы можете проверить это в любое время. как только он ожидается, установите e.Cancel = True и откажитесь от метода.
// метод здесь private void Worker_DoWork (отправитель объекта, DoWorkEventArgs e) {BackgroundWorker bw = (отправитель как BackgroundWorker);
}
источник