Использование таймера для измерения прогресса Parallel.ForEach неожиданно приостанавливается

Я впервые использую Parallel.ForEach, где обрабатываю файлы; в контексте приложения WinForms.

В соответствии с рекомендациями других потоков по этой проблеме кодирования (отчеты о ходе выполнения Parallel.ForEach) у меня есть общедоступное (int) свойство счетчика в моем классе, которое содержит параллельный код и успешно обновляется; У меня также есть таймер в моей форме, который периодически считывает счетчик.

Проблема в том, что когда я выполняю параллельный код, видимое обновление прогресса останавливается, а затем начинается, как только параллельный код завершен.

К сведению: я вызываю параллельный код напрямую, то есть не через фоновый рабочий или асинхронный метод.


person Adrian K    schedule 31.01.2015    source источник


Ответы (2)


Если вы вызываете Parallel.ForEach() из своего потока пользовательского интерфейса (при отсутствии примера кода мы не можем знать наверняка, что происходит), то тот факт, что этот метод останавливается и ожидает завершения всей обработки, не позволит вашему потоку пользовательского интерфейса выполнить любая другая работа, в том числе а) разрешение обработки события таймера и б) разрешение обновления пользовательского интерфейса, даже если таймер был обработан.

Одним из возможных подходов было бы обернуть вызов Parallel.ForEach() вызовом Task.Run(). Например:

private async void button1_Click(object sender, EventArgs e)
{
    // some stuff

    await Task.Run(() => Parallel.ForEach(...));

    // some other stuff
}

В качестве альтернативы вы можете просто выполнить все это как отдельные задачи:

private async void button1_Click(object sender, EventArgs e)
{
    // some stuff

    List<Task> tasks = new List<Task>();

    foreach (...)
    {
        tasks.Add(Task.Run(() => ...));
    }

    await Task.WhenAll(tasks);

    // some other stuff
}

(Приведенные выше примеры не учитывают конкретику, поскольку без примера кода в вопросе невозможно узнать, что на самом деле там будет).

Любой подход должен освободить ваш поток пользовательского интерфейса для обработки обновления прогресса, пока обработка продолжается.

person Peter Duniho    schedule 31.01.2015
comment
Ура, Питер и Йори, позвольте мне поработать в этом направлении. - person Adrian K; 31.01.2015
comment
Спасибо, у меня он работает с асинхронным методом и ожиданием (аналогично вашему первому блоку кода). Я посмотрю, как это пойдет, и рефакторинг по мере необходимости. - person Adrian K; 31.01.2015

Parallel.ForEach на самом деле оценивает запрос параллельно, но ожидает завершения выполнения и блокирует вызывающий поток.

Вы должны использовать его в отдельном потоке/фоновом работнике/задаче, чтобы обновить переменную прогресса, не блокируя пользовательский интерфейс.

person SimpleVar    schedule 31.01.2015