2

Итак, я пишу метод, который должен обрабатывать большое количество строк и обновлять пользовательский интерфейс для каждой обрабатываемой строки. Я использую шаблон async-await для этого, используя репортёр Progress, который сообщает строку в основной поток, где он должен быть обновлен. Проблема в том, что она не работает. Пользовательский интерфейс блокируется, как если бы метод выполнялся синхронно, хотя я использовал ключевое слово ожидания. Вот что мой код выглядит следующим образом:Как сохранить мой асинхронный метод от блокировки пользовательского интерфейса?

private async Task ProcessFile(string filePath, IProgress<string> progress) 
    { 
     string[] LinesToProcess = File.ReadAllLines(filePath); 
     int LineCount = Buffer.Count(); 

     await Task.Factory.StartNew(() => 
     { 
      for (int i = 0; i < Buffer.Count(); i++) 
      { 
       //Do actual processing here 
       progress.Report(string.Format("Lines processed: {0}/{1}", i, LineCount)); 
      } 
     }); 
    } 

А вот метод вызова ProcessFile Task

private async Task RunTask() 
    { 
     string filePath = //Get filePath somehow 
     await ProcessFile(filePath, new Progress<string>(line => 
      { 
       ProcessedLabel.Text = line; 
      })); 
    } 

И, наконец, вот кнопка обратного вызова, что Task RunTask() связан с:

private async void Button_Click(object sender, EventArgs e) 
    { 
     await RunTask(); 
    } 

Я упростил код для удобочитаемости. Любая помощь приветствуется. Спасибо!

+0

'File.ReadAllLines (filePath);' будет делать именно это, читать все строки, поэтому, если есть выделение строк, вам лучше читать строки за строкой. Это может помочь. Я новичок только в Async –

+0

Вы когда-нибудь пытались добавить ConfigureAwait (false) в вызов функции ProcessFile, я видел блоки пользовательского интерфейса из-за этого много раз. – Chasefornone

ответ

0

Я вижу два варианта:

  1. Вы обработку много линий так быстро, что поток пользовательского интерфейса хранятся занято обновлением и не имеет времени на самом деле отображение результатов. Чтобы исправить это, не сообщайте о прогрессе для каждой строки, но только каждые 100, 1000, или если количество строк подходит для вас. В качестве альтернативы вы могли бы сообщать о прогрессе, основанном на времени, например, каждую секунду. Но для этого потребуется более сложный код.
  2. Вы столкнулись с проблемой с Task.Factory.StartNew(), где она работает на потоке пользовательского интерфейса, потому что это CurrentTaskScheduler. Хотя я не вижу никаких указаний в вашем коде, это так, поэтому я не думаю, что это возможно. Но в любом случае вы должны использовать Task.Run(), чтобы быть в безопасности. Для получения дополнительной информации см. StartNew is Dangerous from Stephen Cleary.
+2

Почему downvote? Хотя я согласен, что «Task.Run» лучше, проблема блокировки пользовательского интерфейса почти наверняка связана с (1). Это связано с тем, что [UI-петли сообщений в Windows приоритетны, а 'WM_PAINT' - самый низкий приоритет] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms644927 (v = vs.85) .aspx # quequed_messages). –

+0

Итак, я попробовал оба варианта, и ни одна из них, похоже, не решила проблему. Обновление пользовательского интерфейса каждые 100 или около того циклов 'for' заставляет код работать намного быстрее, но пользовательский интерфейс по-прежнему блокируется. Кроме того, я попытался использовать отладчик, и если бы я установил точку останова в строке 'ProcessedLabel.Text = newReport; ', я вижу, что, перейдя через нее, эта строка вызывается непрерывно, даже если я ее установил обновлять каждые 100 запусков! – CRefice

+0

@CRefice Затем попробуйте 1000, или даже больше. Похоже, что ваша обработка выполняется очень быстро, вы должны установить номер на основе этого. И если вы видите, что строка активна в отладчике, но она не имеет никакого эффекта, то это, скорее всего, просто сбой отладчика. – svick

Смежные вопросы