Параллельная коллекция, отправка пользовательских данных о ходе выполнения в пользовательский интерфейс при параллельном выполнении задач.

У меня есть параллельная коллекция, содержащая 100 тыс. элементов. Обработка каждого элемента в коллекции может занять от 100 мс до 10 секунд. Я хочу ускорить процесс, распараллелив обработку, и чтобы 100 миньонов выполняли работу одновременно. Я также должен сообщать пользовательскому интерфейсу некоторые конкретные данные по мере выполнения этой обработки, а не просто процент выполнения.

Я хочу, чтобы параллельные подзадачи грызли параллельную коллекцию, как стая пескарей, атакующих кусок хлеба, брошенный в пруд. Как предоставить параллельную коллекцию параллельным задачам? Могу ли я иметь обычный цикл и просто запустить асинхронную задачу внутри цикла и передать ее IProgress? Нужна ли мне для этого параллельная коллекция?

Мне было рекомендовано использовать Parallel.ForEach, но я не понимаю, как каждый подпроцесс, установленный степенями параллелизма, может сообщать о пользовательском объекте обратно в пользовательский интерфейс с каждым элементом, который он обрабатывает. , а не только после того, как он закончит обработку своей доли из 100 000 элементов.


person Tim    schedule 01.08.2013    source источник


Ответы (1)


Платформа уже предоставляет интерфейс IProgress для этой цели и реализацию в Ход выполнения. Чтобы сообщить о ходе выполнения, вызовите IProgress.Report с указанием progressvalue. Значение T может быть любого типа, а не только числа.

Каждая реализация IProgress может работать по-своему. Progress вызывает событие и вызывает обратный вызов, который вы передаете ему при его создании.

Кроме того, Progress.Report выполняется асинхронно. Под прикрытием используется SychronizationContext.Post для выполнения своего обратного вызова и всех обработчиков событий в потоке, создавшем экземпляр Progress.

Предполагая, что вы создаете класс значения прогресса следующим образом:

class ProgressValue
{
    public long Step{get;set;}
    public string Message {get;set;}
}

Вы можете написать что-то вроде этого:

IProgress<ProgressValue> myProgress=new Progress<ProgressValue>(p=>
{
    myProgressBar.Value=p.Step;
});

IList<int> myVeryLargeList=...;
Parallel.ForEach(myVeryLargeList,item,state,step=>
{
    //Do some heavy work
    myProgress.Report(new ProgressValue
    {
         Step=step,
         Message=String.Format("Processed step {0}",step);
    });
});

ИЗМЕНИТЬ

Ой! Progress явно реализует IProgress. Вы должны передать его IProgress , как заметил @Tim.

Исправлен код для явного объявления myProgress как IProgress.

person Panagiotis Kanavos    schedule 02.08.2013
comment
Может ли тяжелая работа быть синхронным методом? Можно ли ожидать асинхронный метод внутри Parallel.ForEach? - person Tim; 02.08.2013
comment
Parallel.Foreach предназначен для параллельного выполнения синхронных заданий над большим объемом данных. Вы можете await внутри Parallel.ForEach, добавив ключевое слово async перед лямбда-объявлением, но вам, вероятно, следует переосмыслить это, поскольку Parallel.Foreach в конечном итоге просто создаст и ожидает выполнения задач. Лучшим решением было бы использовать конвейер параллельных шагов для обработки данных, сгенерированных на предыдущем шаге. TPL Dataflow — одна из реализаций этого - person Panagiotis Kanavos; 02.08.2013
comment
Я получаю сообщение об ошибке myProgress does not contain a definition for 'Report' при вызове myProgress.Report(.. - person Tim; 03.08.2013
comment
Догадаться; нужно передать в IProgress: (myProgress as IProgress<myCustomProgressType>).Report() - person Tim; 03.08.2013
comment
Этот подход, когда синхронный метод вызывается в параллельном цикле, приводит к тому, что пользовательский интерфейс перестает отвечать на запросы. Форму нельзя перетащить в другое место на экране во время обработки цикла. - person Tim; 03.08.2013
comment
Progress.Report работает асинхронно, в этом вся суть. Под прикрытием, которое он использует, если ваш пользовательский интерфейс блокирует, должна быть другая проблема. - person Panagiotis Kanavos; 05.08.2013