2016-08-01 2 views
0

У меня есть ниже блок кода, который в настоящее время запущен с помощью таймера:Правильный способ использовать Task.Run и подождите

private void CheckForScheduleItemsTimerOnElapsed(Object sender, ElapsedEventArgs elapsedEventArgs) 
{ 
    if (CurrentlyProcessingMsgs) 
     return; 

    CurrentlyProcessingMsgs = true; 

    var task = Task.Run(() => { 
      CheckForScheduleItems.Process(_cancellationToken); 
     }, _cancellationToken); 

    task.Wait(_cancellationToken); 

    CurrentlyProcessingMsgs = false; 
} 

Теперь проблема, что я имею, что в некоторых случаях, линия

CurrentlyProcessingMsgs = false; 

не набирается. Насколько я понимаю, Task.Wait должен был дождаться завершения задачи, а затем продолжить, тем самым сбросив этот флаг. Но в некоторых случаях этого не происходит. (Примечание: этот таймер управляется службой.) Что происходит, так это то, что при завершении работы я проверяю значение CurrentProcessingMsgs, и иногда это все еще верно после завершения задачи.

Цель состоит в том, чтобы иметь несколько таймеров, которые срабатывают, как показано выше. Каждый таймер будет обрабатывать аналогично тому, что показано выше, и я хотел бы запускать каждый процесс в отдельном потоке. Я ошибался в своем понимании CancellationToken, поэтому в этом случае было бы правильным способом настроить таймеры и задачи для запуска этих методов и каждого из них в отдельном потоке?

+0

Для ваших таймеров вопрос зависит от используемого вами таймера. Некоторые таймеры, встроенные в .NET, автоматически будут использовать пул потоков для запуска таймера (то же самое, что делает Task.Run). Вы должны показать нам, какой тип таймера вы используете. Возможно, вы уже делаете то, что хотите. –

+0

Я использую класс System.Timers.Timer, чтобы отключить таймер. –

+0

Значение ['Timer.SynchronizingObject'] (https://msdn.microsoft.com/en-us/library/system.timers.timer.synchronizingobject (v = vs.110) .aspx), установленное на нем? Если это «null», ваш таймер уже создает новые потоки для запуска тиков таймера, вам не нужно ничего делать. –

ответ

1

Если _cancellationToken аннулировано, будет произведено исключение. Если вы хотите, чтобы убедиться, что флаг будет изменен, даже если он будет отменен или какое-либо другое возбуждается исключение Process положить CurrentlyProcessingMsgs = false; в попытке /, наконец, блок

private void CheckForScheduleItemsTimerOnElapsed(Object sender, ElapsedEventArgs elapsedEventArgs) 
{ 
    if (CurrentlyProcessingMsgs) 
     return; 

    CurrentlyProcessingMsgs = true; 
    try 
    { 
     var task = Task.Run(() => { 
       CheckForScheduleItems.Process(_cancellationToken); 
      }, _cancellationToken); 

     task.Wait(_cancellationToken); 
    } 
    finally 
    { 
     CurrentlyProcessingMsgs = false; 
    } 
} 

P.S. Существует почти никогда не веская причина, чтобы сделать Task.Run, а затем сразу же ждать его. Было бы намного меньше накладных расходов, чтобы просто сделать следующее, которое должно иметь очень похожую логику.

private void CheckForScheduleItemsTimerOnElapsed(Object sender, ElapsedEventArgs elapsedEventArgs) 
{ 
    if (CurrentlyProcessingMsgs) 
     return; 

    CurrentlyProcessingMsgs = true; 
    try 
    { 
     _cancellationToken.ThrowIfCancellationRequested(); 
     CheckForScheduleItems.Process(_cancellationToken); 
    } 
    finally 
    { 
     CurrentlyProcessingMsgs = false; 
    } 
} 

Вот обновленная версия с отлов исключения.

private void CheckForScheduleItemsTimerOnElapsed(Object sender, ElapsedEventArgs elapsedEventArgs) 
{ 
    if (CurrentlyProcessingMsgs) 
     return; 

    CurrentlyProcessingMsgs = true; 
    try 
    { 
     _cancellationToken.ThrowIfCancellationRequested(); 
     CheckForScheduleItems.Process(_cancellationToken); 
    } 
    catch (OperationCanceledException ex) 
    { 
     //If the task was canceled for some other reason than our token raise the exception. 
     if(ex.CancellationToken != _cancellationToken) 
      throw; 
    } 
    finally 
    { 
     CurrentlyProcessingMsgs = false; 
    } 
} 
+0

Вы запускаете один из других потоков, а затем немедленно блокируете текущий поток. Здесь нет дополнительной параллельной работы, все, что вы делаете, это использование дополнительного бара для дополнительного стека вызовов. Также на токене, когда вы 'Wait' переходите на' OperationCanceledException', если вы хотите изящную остановку, вам нужно поймать исключение. –

+0

Я обновил свой вопрос. Я, очевидно, неправильно понимаю некоторую информацию о том, как правильно использовать TPL. –

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