2014-09-13 53 views
3

我想用一些可以處理異步函數的東西來替換舊的Threading.Timer代碼。我這樣做的原因是你不能將一個Threading.Timer傳遞給一個異步函數而不把它設置爲一個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#,所以無論是作爲答案都可以。

編輯1:這需要是線程安全的。這是我擔心的原因。

+0

這應該是線程安全的嗎?因爲它不是,如果它不應該,你不需要擔心競爭條件。 – i3arnon 2014-09-13 13:35:26

+0

@ I3arnon不幸的是,它確實需要線程安全,這可能嗎?編輯提出這個問題。 – iguanaman 2014-09-13 13:38:26

+0

@ I3arnon我明白,但它確實需要是線程安全的,這就是爲什麼我特別關心這一點。 – iguanaman 2014-09-13 13:47:42

回答

2

計時器滴答排隊到線程池。您可以毫無問題地使用asyncawait。使您的滴答函數調用async Task函數並丟棄結果任務。和文件,爲什麼。

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

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

請注意,計時器滴答可以任意延遲,它們可以併發執行,並且可以在停止計時器後運行。

我不明白你爲什麼需要使用異步代碼。您已經在線程池中,因此您不必解除對UI的阻止。寫入本地磁盤並不會從異步IO中受益。也許答案是:只需在這裏使用同步代碼。

爲了防止覆蓋CancellationToken,您需要將其複製到所有使用它的地方。沒有什麼應該直接使用該領域。

更好的是,創建一個AsyncTimer的新實例,而不是使其可重新啓動。避免狀態突變。

+0

感謝您的迴應。首先,我不確定你的意思是「讓你的滴答函數調用一個異步任務函數並拋棄結果任務」。如在,將任務分配給一個對象來啓動它,讓子結束?是不是很差的做法,關於錯誤處理等?雖然我知道寫入本地磁盤可能不保證異步,但這只是我正在考慮替換的計時器代碼的一個示例。作爲另一個例子,我也有通過HttpClient發送Web請求的定時器。用你的最後一點,我不知道如何實施。 – iguanaman 2014-09-13 20:23:11

+0

...不知道如何實施。該實例需要對兩個不同的子例程可見,所以說一個類的私有屬性。當我想再次啓動定時器時,我必須在現有的實例上創建一個新的實例,並且遇到相同的問題,除此之外可能更糟糕,因爲我覆蓋整個實例,而不僅僅是取消令牌。 – iguanaman 2014-09-13 20:27:41

+0

我添加了代碼。只要您讓任務本身處理錯誤,您就可以安全地放棄任務。這與同步任務處理程序沒有區別:您必須捕獲錯誤。 – usr 2014-09-13 20:29:01