2017-01-08 9 views
2

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

Вообще говоря, я наткнулся на использование async/awaitin this answer, а также про различные таймеры in this answer.

Мой сценарий следующий: я хочу читать данные каждые 250 мс и обновлять свой пользовательский интерфейс на основе полученных данных. Наивный подход был бы очень простой вызов, как это:

while (true) { 
    // do stuff 
    Thread.Sleep(250); 
} 

Этот код не будет называться каждые 250мс в зависимости от того, как долго выполнение в теле на самом деле взял! Кроме того, в отличие от ответа на конкретный таймер, я не хочу дождаться завершения этого метода, чтобы вызвать его снова, поэтому, если метод окажется слишком длинным для одного тика, я все равно хочу называть его каждые 250 мс как можно точнее и прерывать при необходимости, предыдущее выполнение галочки.

Должен ли я использовать общий Timer, a DispatcherTimer, a Task и т. Д.? У вас есть дополнительные ссылки и связанные с ними вопросы, которые актуальны?

+0

В методе 'async' вы можете попробовать' await Task.Delay (250) '. – devRicher

+0

Итак, если метод занимает больше 250 мс, вы хотите прервать и запустить новый прогон. Но если многие звонки занимают больше 250 мс, у вас не будет доработок. Я не вижу, как вы хотите добиться результата в таких случаях, как 250 мс. –

+0

Метод не предназначен для возврата результата в конце, но меньших результатов во время выполнения. Кроме того, в моем конкретном случае использования метод будет занимать дольше 250 мс в очень исключительных ситуациях, когда система имеет своего рода отставание/hickup. Я получаю данные в порядке, где могу сказать, что результаты на раннем этапе вызова более важны, чем те, которые в конце, поэтому я хочу, чтобы первые куски чаще обновлялись в ситуации, когда метод не заканчивается. - Есть ли у вас идеи о совершенно разных подходах к этому? –

ответ

1

Так что, если я правильно понимаю, вы хотите запустить код за каждые 250 мс. Даже если код занимает 40 мс, ожидание 250 мс с использованием Thread.Sleep(250) нежелательно, потому что в общей сложности это займет 290 мс.

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

const int MaxAllowTimeInMs = 250; 

async Task Main() 
{ 
    while(true) 
    { 
     var cts = new CancellationTokenSource(); 
     cts.CancelAfter(MaxAllowTimeInMs); 

     try 
     {   
      await Task.WhenAll(new[] 
      { 
       MyTask(cts.Token), 
       Task.Delay(MaxAllowTimeInMs) 
      }); 
     } 
     catch (OperationCanceledException) 
     { 
      // MyTask took longer than 250ms 
     } 
    } 
} 

async Task MyTask(CancellationToken ct) 
{ 
    // Get some data 
    ... 

    ct.ThrowIfCancellationRequested(); 

    // If you reach this point, you can process stuff knowing that it took less than 250ms to get it. 
} 

Этот код позволяет MyTask завершить в 250ms. Если это займет больше времени, результат будет отброшен.

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

Используя код Timer, вы можете использовать такой же подход. В виде псевдокода:

const int MaxAllowTimeInMs = 250; 

void Main() 
{ 
    var timer = new DispatcherTimer(); 
    timer.Interval = TimeSpan.FromMilliseconds(MaxAllowTimeInMs); 
    timer.Tick += async (s, e) => 
    { 
     using(var cts = new CancellationTokenSource()) 
     { 
      try 
      { 
       cts.CancelAfter(MaxAllowTimeInMs); 
       await MyTask(cts.Token); 
      } 
      catch (OperationCanceledException) 
      { 
       // MyTask took longer than 205ms 
      } 
     } 
    }; 
    timer.Start(); 
} 

async Task MyTask(CancellationToken ct) 
{ 
    // Simulate some work 
    ... 
    ct.ThrowIfCancellationRequested(); 
} 
+0

Вы имели в виду 'Task.WhenAny'? –

+0

@BenCottrell Нет, не совсем. Потому что «Task.WhenAny» приведет к нескольким вызовам «MyTask» в 250 мс. «WhenAll» гарантирует, что между каждым вызовом будет 250 мс. –