Ответ 1
Обычно я рекомендую Task
и/или await
при использовании .NET 4.5. Но Task
и BGW имеют 2 совершенно разных сценария. Задача хороша для общих коротких асинхронных задач, которые могут быть привязаны к продолжению и ждут хороших задач, которые неявно перестраиваются обратно в поток пользовательского интерфейса. BGW подходит для одной длительной операции, которая не должна влиять на отзывчивость вашего пользовательского интерфейса. Вы можете перетащить BGW на поверхность дизайна и дважды щелкнуть, чтобы создать обработчики событий. Вам не нужно иметь дело с LongRunning
или ConfigureAwait
, если вы не хотите маршалировать другой поток. Многие находят прогресс BGW легче, чем IProgress<T>
.
Вот несколько примеров использования обоих сценариев "продолжительной операции":
Поскольку в конкретном вопросе упоминается .NET 4.0, следующий простой код, который использует Task
для выполнения длительной операции при обеспечении прогресса в пользовательском интерфейсе:
startButton.Enabled = false;
var task = Task.Factory.
StartNew(() =>
{
foreach (var x in Enumerable.Range(1, 10))
{
var progress = x*10;
Thread.Sleep(500); // fake work
BeginInvoke((Action) delegate {
progressBar1.Value = progress;
});
}
}, TaskCreationOptions.LongRunning)
.ContinueWith(t =>
{
startButton.Enabled = true;
progressBar1.Value = 0;
});
Аналогичный код с BackgroundWorker
может быть:
startButton.Enabled = false;
BackgroundWorker bgw = new BackgroundWorker { WorkerReportsProgress = true };
bgw.ProgressChanged += (sender, args) =>
{ progressBar1.Value = args.ProgressPercentage; };
bgw.RunWorkerCompleted += (sender, args) =>
{
startButton.Enabled = true;
progressBar1.Value = 0;
};
bgw.DoWork += (sender, args) =>
{
foreach (var x in Enumerable.Range(1, 10))
{
Thread.Sleep(500);
((BackgroundWorker)sender).ReportProgress(x * 10);
}
};
bgw.RunWorkerAsync();
Теперь, если вы использовали .NET 4.5, вы можете использовать Progress<T>
вместо вызова BeginInvoke
с Task
. И поскольку в 4.5, использование await
, скорее всего, будет более читаемым:
startButton.Enabled = false;
var pr = new Progress<int>();
pr.ProgressChanged += (o, i) => progressBar1.Value = i;
await Task.Factory.
StartNew(() =>
{
foreach (var x in Enumerable.Range(1, 10))
{
Thread.Sleep(500); // fake work
((IProgress<int>) pr).Report(x*10);
}
}, TaskCreationOptions.LongRunning);
startButton.Enabled = true;
progressBar1.Value = 0;
Использование Progress<T>
означает, что код не связан с определенным интерфейсом пользовательского интерфейса (т.е. вызов BeginInvoke
) во многом таким же образом, что BackgroundWorker
облегчает развязку из определенной структуры пользовательского интерфейса. Если вам все равно, то вам не нужно вводить дополнительную сложность использования Progress<T>
Что касается LongRunning
, как говорит Стивен Туб: "Обычно вы используете LongRunning только в том случае, если вы обнаружили, что тестирование производительности не использует его, что приводит к длительным задержкам в обработке другой работы", поэтому, если вы считаете нужным чтобы использовать его, тогда вы его используете - там добавлен анализ или просто "сложность" всегда добавления параметра LongRunning
. Не использование LongRunning означает, что поток потока потоков, используемый для продолжительной работы, не будет использоваться для других, более сложных задач и может заставить пул потоков задерживать запуск одной из этих переходных задач, пока он запускает другой поток (по крайней мере, второй).
В структуре нет атрибутов, которые специально говорят, что BGW (или EAP или APM) устарели. Итак, вам решать, где и когда какая-либо из этих вещей "устарела". В BGW, в частности, всегда был очень специфический сценарий использования, который по-прежнему применяется к нему. У вас достаточно достойные альтернативы в .NET 4.0 и 4.5; но я действительно не думаю, что BGW "устарела".
Я не говорю, что всегда использую BackgroundWorker
, я просто говорю, думаю, прежде чем вы автоматически откажетесь от BackgroundWorker, в некоторых случаях это может быть лучший выбор.