2014-09-13 3 views
3

Я пытаюсь заменить старый код Threading.Timer на то, что может обрабатывать функцию async. Причина, по которой я делаю это, заключается в том, что вы не можете передавать Threading.Timer функцию async, не делая ее Async Sub, которую я должен понимать, это плохая идея.Замена Threading.Timer с пользовательским классом таймера асинхронизации?

Код, который я хочу заменить;

Dim SaveTimer as New Threading.Timer(AddressOf SaveToFile, Nothing, 1000 * 60 * 5, 1000 * 60 * 5) 

SaveToFile является функцией асинхронной, так что я получаю «Целевой вернулся из этой асинхронной функции будет удалена, и какое-либо исключение в нем игнорируется. Рассмотрит изменение его к югу асинхронного поэтому его исключения распространяются» предупреждение ,

Цель этого таймера - сохранить содержимое класса, в котором он находится, в файл каждые 5 минут. Тем не менее, я также иногда вызываю SaveToFile вручную, и если я это сделаю, я хочу сбросить таймер.

С Threading.Timer Я бы сделал это;

Public Async Function ManuallySave As Tasks.Task 
    SaveTimer.Change(Timeout.Infinite, Timeout.Infinite) 
    Await SaveToFile 
    SaveTimer.Change(1000 * 60 * 5, 1000 * 60 * 5) 
    End Function 

Теперь я знаю, как планировать что-то должно быть сделано с использованием задач Task.Delay, но я не знаю, как остановить его и сбросить.

Вот моя попытка класса асинхронного таймера;

Public Class AsyncTimer 

    Private CancellationToken As New CancellationTokenSource 
    Private DelegateAction As Action(Of Tasks.Task) = Nothing 

    Public Sub New(DelegateAction As Action(Of Tasks.Task)) 
    Me.DelegateAction = DelegateAction 
    End Sub 

    Public Async Function StartTimer(Interval As Integer, Optional Repeat As Boolean = False) As Tasks.Task 
    CancellationToken = New CancellationTokenSource 
    Do 
     Await Tasks.Task.Delay(Interval, CancellationToken.Token).ContinueWith(DelegateAction, CancellationToken.Token, TaskContinuationOptions.NotOnCanceled, TaskScheduler.Current) 
    Loop Until Not Repeat OrElse CancellationToken.IsCancellationRequested 
    End Function 

    Public Sub StopTimer() 
    CancellationToken.Cancel() 
    End Sub 

End Class 

Что касается меня, то, что я называю StopTimer, то вскоре после этого StartTimer, который заменяет CancellationToken с новым экземпляром. Я беспокоюсь, что до отмены маркера вступает в силу, я переписываю его.

Каково решение этой проблемы?

Я знаю, что код находится в VB, но я понимаю C# так же хорошо, так как ответ будет в порядке.

EDIT1: Это должно быть потокобезопасным. Это является причиной моей озабоченности.

+0

Должен ли он быть потокобезопасным? Потому что это не так, и если это не так, вам не нужно беспокоиться о гоночных условиях. – i3arnon

+0

@ I3arnon Нужно ли быть потокобезопасным, к сожалению, возможно? Отредактировал вопрос, чтобы упомянуть об этом. – iguanaman

+0

@ I3arnon Я понимаю, но он действительно должен быть потокобезопасным, поэтому я беспокоюсь об этом конкретно. – iguanaman

ответ

2

Timer ticks поставлены в очередь на поток-пул. Вы можете использовать async и await там без проблем. Сделайте свою функцию галочки функцией async Task и выбросьте полученную задачу. И документ, почему.

void TimerProc(...) { 
var task = TimerImpl(); 
//throw away task 
} 

async Task TimerImpl() { 
try { MyCode(); } 
catch (Exception ex) { Log(ex); } 
} 

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

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

Для защиты от перезаписи CancellationToken вам необходимо скопировать его во все используемые им места. Ничто не должно напрямую использовать поле.

Еще лучше, создайте новый экземпляр AsyncTimer вместо того, чтобы сделать его перезагружаемым. Избегайте мутации состояния.

+0

Спасибо за ответ. Во-первых, я не уверен, что вы подразумеваете под «Сделать вашу функцию галочки вызов функции async Task и выбросить результирующую задачу». Как и в, назначьте задачу объекту, чтобы запустить его, и дать конец? Разве это не плохая практика, касающаяся обработки ошибок и т. Д.? Хотя я знаю, что запись на локальные диски может не оправдывать асинхронность, это всего лишь один пример кода таймера, который я рассматриваю при замене. У меня также есть таймеры, которые отправляют веб-запросы через HttpClient, в качестве другого примера. С вашей последней точкой, я не уверен, как реализовать. – iguanaman

+0

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

+0

Я добавил код. Вы можете безопасно отбросить задачу до тех пор, пока сама задача обрабатывает ошибки. Это не отличается от синхронных обработчиков задач: вы должны ловить ошибки. – usr

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