Я пытаюсь лучше понять параметры Async и Parallel, которые у меня есть на С#. В нижеприведенных фрагментах я включил 5 подходов, которые я встречал больше всего. Но я не уверен, что выбрать - или еще лучше, какие критерии следует учитывать при выборе:
Метод 1: Задача
(см. http://msdn.microsoft.com/en-us/library/dd321439.aspx)
Вызов StartNew функционально эквивалентен созданию задачи с использованием одного из ее конструкторов, а затем вызывает Start, чтобы запланировать ее выполнение. Однако, если создание и планирование не должны быть разделены, StartNew - это рекомендуемый подход как для простоты, так и для производительности.
TaskFactory. Метод StartNew должен быть предпочтительным механизмом для создания и планирования вычислительных задач, но для сценариев, в которых создание и планирование должны быть разделены, могут использоваться конструкторы, а затем метод запуска задачи может использоваться для планирования выполнения задачи в более позднее время.
// using System.Threading.Tasks.Task.Factory
void Do_1()
{
var _List = GetList();
_List.ForEach(i => Task.Factory.StartNew(_ => { DoSomething(i); }));
}
Метод 2: QueueUserWorkItem
(см. http://msdn.microsoft.com/en-us/library/system.threading.threadpool.getmaxthreads.aspx)
Вы можете поставить в очередь столько запросов пула потоков, сколько позволяет системная память. Если запросов больше, чем потоков пула потоков, дополнительные запросы остаются в очереди до тех пор, пока потоки пула потоков не станут доступными.
Вы можете поместить данные, требуемые методом очереди, в поля экземпляра класса, в котором этот метод определен, или вы можете использовать перегрузку QueueUserWorkItem (WaitCallback, Object), которая принимает объект, содержащий необходимые данные.
// using System.Threading.ThreadPool
void Do_2()
{
var _List = GetList();
var _Action = new WaitCallback((o) => { DoSomething(o); });
_List.ForEach(x => ThreadPool.QueueUserWorkItem(_Action));
}
Метод 3: Параллельный .Foreach
(см.: http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallel.foreach.aspx)
Класс Parallel предоставляет параллельные замены данных на основе библиотек для общих операций, таких как для циклов, для каждой петли и выполнения набора инструкций.
Делегатор тела вызывается один раз для каждого элемента в источнике, который можно перечислить. Он снабжен текущим элементом как параметром.
// using System.Threading.Tasks.Parallel
void Do_3()
{
var _List = GetList();
var _Action = new Action<object>((o) => { DoSomething(o); });
Parallel.ForEach(_List, _Action);
}
Метод 4: IAsync.BeginInvoke
(см.: http://msdn.microsoft.com/en-us/library/cc190824.aspx)
BeginInvoke является асинхронным; поэтому управление немедленно возвращается к вызывающему объекту после его вызова.
// using IAsync.BeginInvoke()
void Do_4()
{
var _List = GetList();
var _Action = new Action<object>((o) => { DoSomething(o); });
_List.ForEach(x => _Action.BeginInvoke(x, null, null));
}
Метод 5: BackgroundWorker
(см.: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx)
Чтобы настроить фоновую операцию, добавьте обработчик событий для события DoWork. Вызовите свою трудоемкую операцию в этом обработчике событий. Чтобы запустить операцию, вызовите RunWorkerAsync. Чтобы получать уведомления об обновлениях прогресса, обработайте событие ProgressChanged. Чтобы получать уведомление, когда операция завершена, обработайте событие RunWorkerCompleted.
// using System.ComponentModel.BackgroundWorker
void Do_5()
{
var _List = GetList();
using (BackgroundWorker _Worker = new BackgroundWorker())
{
_Worker.DoWork += (s, arg) =>
{
arg.Result = arg.Argument;
DoSomething(arg.Argument);
};
_Worker.RunWorkerCompleted += (s, arg) =>
{
_List.Remove(arg.Result);
if (_List.Any())
_Worker.RunWorkerAsync(_List[0]);
};
if (_List.Any())
_Worker.RunWorkerAsync(_List[0]);
}
}
Я полагаю, что очевидная критика будет:
- Лучше, чем другой для производительности?
- Лучше, чем другой для обработки ошибок?
- Лучше, чем другой для мониторинга/обратной связи?
Но как выбрать вы? Заранее благодарим за понимание.