Использование Parallel.ForEach против TPL.Dataflow или другого решения для применения OCR к большому количеству изображений - PullRequest
0 голосов
/ 25 апреля 2019

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

Интересно, так ли это лучше? Я читал о TPL.Dataflow, и, хотя он выглядит излишним, я думаю, что лучше использовать более сложный подход, поскольку я буду потенциально обрабатывать сотни файлов одновременно, и я не уверен, что сотня задач будет создана это хорошая практика. Кроме того, я читал, что это плохая практика - использовать Interlocked.Increment в Parallel.ForEach. Должен ли я конвертировать это в Parallel.For? Ниже моя текущая реализация:

private async void BatchOCR_Click(object sender, EventArgs e) {
   //some UI stuff
   source = new CancellationTokenSource();
   progressBar.Value = 0;
   int counter = 0;
   IProgress<int> progress = new Progress<int>(i => { progressBar.Value = (int)Math.Round((float)(i)*100 / fileList.Items.Count, 0); });

   await Task.Run(() => RunBatchOCR(ListOfPrintouts,progress,counter), source.Token);
   //some UI stuff
}
private async Task RunBatchOCR(List<Printout> printouts,IProgress<int> progress, int counter) {
   progress.Report(0);
   Parallel.ForEach(printouts, (printout,state) =>
      {
         try
         {
            source.Token.ThrowIfCancellationRequested();
         }
         catch
         {
            Console.WriteLine("Task was cancelled");
            cancelButton.Enabled = false;
            state.Break();
          }
          finally
          {
             Interlocked.Increment(ref counter);
          }
          printout.OcrHelper.runOCR(); //loads bitmap and extracts text
          progress.Report(counter);
          Console.WriteLine(counter.ToString());
    });
}

1 Ответ

1 голос
/ 25 апреля 2019

Я не уверен, что создание сотен задач - это хорошая практика

Это нормально.Parallel использует интеллектуальное разбиение.

Что касается остальной части кода, Interlocked хорошо использовать в качестве счетчика, но вы не хотите получать доступ к той же переменной без блокировкибарьеры.И этот код CancellationToken требует упрощения:

private async Task RunBatchOCR(List<Printout> printouts, IProgress<int> progress)
{
  int counter = 0;
  progress?.Report(0);
  try
  {
    Parallel.ForEach(printouts, new ParallelOptions { CancellationToken = source.Token }, printout =>
    {
      printout.OcrHelper.runOCR(); //loads bitmap and extracts text
      var update = Interlocked.Increment(ref counter);
      progress?.Report(update);
      Console.WriteLine(update.ToString());
    });
  }
  catch (OperationCanceledException)
  {
    Console.WriteLine("Task was cancelled");
    cancelButton.Enabled = false;
  }
}
...