2012-06-14 3 views
0

У меня есть приложение для обработки хвостов, которое выполняет поток рабочего потока в инфинитивном цикле и проверяет последние записи внутри некоторого TXT-файла. Когда он найдет новые записи, я использую Dispatcher.Invoke для обновления TextBox на экране с последней записью, добавленной в текстовый файл.Dispatcher.Invoke loop freeze UI

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

Удивительно, если есть какое-либо обходное решение. Я мог бы получить большие куски из текстового файла, но не хочу испортить работу с журналом в реальном времени, также не хочу задерживать запись строк в TextBox потоком пользовательского интерфейса, поскольку это приведет к тому, что мое приложение не синхронизируется с фактическими данными внутри исходный текстовый файл.

Вот метод DoWork фона рабочего

private void worker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    reader = new StreamReader(new FileStream(file.File, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); 
    lastMaxOffset = reader.BaseStream.Length; 

    while (true) 
    { 
    if (worker.CancellationPending) 
    { 
     e.Cancel = true; 
     break; 
    }     

    if (reader.BaseStream.Length == lastMaxOffset) 
     continue; 

    reader.BaseStream.Seek(lastMaxOffset, SeekOrigin.Begin); 

    string line = ""; 
    while ((line = reader.ReadLine()) != null) 

    this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, (Action)(() => 
    { 
     textLog.Text += "\r" + line; 
     scrollViewer.ScrollToBottom(); 
    })); 

    lastMaxOffset = reader.BaseStream.Position;   
    } 
} 

, как вы можете увидеть UI нить просто добавить текст в TextBox и когда это случается часто интерфейс замерзаете

ответ

1

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

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

Для того, чтобы ускорить работу, вам нужно сделать что-то, что буферизирует данные, и отправляет их в более крупные куски. В этом случае, так как вы ищете новые записи в журнале, я бы рекомендовал прочитать ВСЕ записи журнала в конце файла сразу и объединить весь фрагмент обратно в свой пользовательский интерфейс (вместо того, чтобы делать это по очереди).

7

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

Вместо этого вы должны использовать Dispatcher.BeginInvoke, который ставит в очередь работу над потоком пользовательского интерфейса, но не блокирует. Это будет немного лучше, как если бы вы делали это слишком много в течение короткого периода времени, вы все еще наводняете поток пользовательского интерфейса, чтобы выполнить его, и он потратит много времени на выполнение этой работы.

Вместо этого наилучшим подходом будет очередь этих изменений в потоке пользовательского интерфейса, а затем, когда очередь достигает определенного предела (то есть, 100 новых строк текста) или превышает определенный промежуток времени (скажем, 200 мс) затем позвоните Dispatcher.BeginInvoke, чтобы отправить эти изменения в пользовательский интерфейс. Это даст вам лучший отклик пользователя.

1

Извините, но чтение файла в бесконечном цикле является ошибочным и ... ну ... немым. Есть четко определенные классы, такие как FileSystemWatcher, нет необходимости ставить 100% -ную нагрузку на процессорное ядро ​​только потому, что вы хотите обновления в реальном времени вашего файла.

я понимаю, что вы совершенно новой для программирования - ну, все мы делаем ошибки и должны учиться, принимать эти советы:

  • Бесконечный цикл автоматически ПРОГРАММИРОВАНИЕ ошибки
  • Если петли на самом деле требуется, можно было бы рекомендовать, чтобы согласовывая нить сна между каждой итерации
0

Я согласен с ответом по CodingGorilla.

Использование Dispatcher.Invoke приведет к синхронному вызову.

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

Вызов будет заблокирован до тех пор, пока пользовательский интерфейс не выполнит запрошенную задачу, и если это произойдет быстро, ваш поток пользовательского интерфейса заблокирует и, следовательно, вы не получите никаких обновлений.

Попробуйте заменить Dispatcher.Invioke с Dispatcher.BeginInvoke и посмотреть, если это решит проблему.

// Load in background 
this.Dispatcher.BeginInvoke(new Action(() => 
{ 
    textLog.Text += "\r" + line; 
    scrollViewer.ScrollToBottom(); 

})); 

Также, как было предложено, вы можете поставить петлю в режим ожидания в течение 1 миллисекунды; используя Thread.Sleep (1); каждый так часто, чтобы вы не закончили работу с процессором.

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