(因爲你正在使用.NET 4.5,它使用Task
內部)我會使用TPL Dataflow這一點。您可以輕鬆創建一個ActionBlock<TInput>
,它在處理它的操作並等待適當的時間後將項目發佈到自身。
首先,建立一個工廠,將創建永無止境的任務:
ITargetBlock<DateTimeOffset> CreateNeverEndingTask(
Action<DateTimeOffset> action, CancellationToken cancellationToken)
{
// Validate parameters.
if (action == null) throw new ArgumentNullException("action");
// Declare the block variable, it needs to be captured.
ActionBlock<DateTimeOffset> block = null;
// Create the block, it will call itself, so
// you need to separate the declaration and
// the assignment.
// Async so you can wait easily when the
// delay comes.
block = new ActionBlock<DateTimeOffset>(async now => {
// Perform the action.
action(now);
// Wait.
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken).
// Doing this here because synchronization context more than
// likely *doesn't* need to be captured for the continuation
// here. As a matter of fact, that would be downright
// dangerous.
ConfigureAwait(false);
// Post the action back to the block.
block.Post(DateTimeOffset.Now);
}, new ExecutionDataflowBlockOptions {
CancellationToken = cancellationToken
});
// Return the block.
return block;
}
我選擇了ActionBlock<TInput>
採取DateTimeOffset
structure;你必須傳遞一個類型參數,並且它可能會傳遞一些有用的狀態(如果你願意,你可以改變狀態的性質)。
而且,請注意,ActionBlock<TInput>
在默認情況下只處理一個項目的時間,所以你保證只有一個動作將被處理(意思是,你將不必應付reentrancy當它調用Post
extension method)。
我也已將CancellationToken
structure傳遞給ActionBlock<TInput>
的構造函數和Task.Delay
method調用;如果過程取消,取消將在第一個可能的機會進行。
從那裏,它是你的代碼來存儲ActionBlock<TInput>
實現(這是代表都是消費者塊的更高層次的抽象,並希望能夠通過對呼叫觸發消費ITargetBlock<DateTimeoffset>
interface的簡單重構Post
擴展方法):
CancellationTokenSource wtoken;
ActionBlock<DateTimeOffset> task;
你StartWork
方法:
void StartWork()
{
// Create the token source.
wtoken = new CancellationTokenSource();
// Set the task.
task = CreateNeverEndingTask(now => DoWork(), wtoken.Token);
// Start the task. Post the time.
task.Post(DateTimeOffset.Now);
}
然後你StopWork
方法:
void StopWork()
{
// CancellationTokenSource implements IDisposable.
using (wtoken)
{
// Cancel. This will cancel the task.
wtoken.Cancel();
}
// Set everything to null, since the references
// are on the class level and keeping them around
// is holding onto invalid state.
wtoken = null;
task = null;
}
爲什麼要在這裏使用TPL Dataflow?有幾個原因:
關注
的CreateNeverEndingTask
方法分離現在是一個工廠,創建你的「服務」可以這麼說。您可以控制何時啓動和停止,並且它是完全獨立的。您不必將定時器的狀態控制與代碼的其他方面交織在一起。您只需創建塊,啓動它,並在完成後停止。
更有效地利用的線程/任務/資源
在TPL數據流中的塊的默認調度爲Task
,這是線程池相同。通過使用ActionBlock<TInput>
來處理您的操作以及致電Task.Delay
,您可以控制在實際沒有做任何事情時使用的線程。當然,當你產生新的Task
來處理延續時,這實際上會導致一些開銷,但是這應該很小,因爲你沒有在緊密的循環中處理這個(你在調用之間等待10秒)。
如果DoWork
函數實際上可製成awaitable(即,在它返回一個Task
),然後就可以(可能)優化此更加通過調整上述工廠方法採取Func<DateTimeOffset, CancellationToken, Task>
代替Action<DateTimeOffset>
,像所以:
ITargetBlock<DateTimeOffset> CreateNeverEndingTask(
Func<DateTimeOffset, CancellationToken, Task> action,
CancellationToken cancellationToken)
{
// Validate parameters.
if (action == null) throw new ArgumentNullException("action");
// Declare the block variable, it needs to be captured.
ActionBlock<DateTimeOffset> block = null;
// Create the block, it will call itself, so
// you need to separate the declaration and
// the assignment.
// Async so you can wait easily when the
// delay comes.
block = new ActionBlock<DateTimeOffset>(async now => {
// Perform the action. Wait on the result.
await action(now, cancellationToken).
// Doing this here because synchronization context more than
// likely *doesn't* need to be captured for the continuation
// here. As a matter of fact, that would be downright
// dangerous.
ConfigureAwait(false);
// Wait.
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken).
// Same as above.
ConfigureAwait(false);
// Post the action back to the block.
block.Post(DateTimeOffset.Now);
}, new ExecutionDataflowBlockOptions {
CancellationToken = cancellationToken
});
// Return the block.
return block;
}
當然,這將是很好的做法,編織CancellationToken
通過你的方法(如果它接受一個),這是在這裏完成。
這意味着,你將有一個DoWorkAsync
方法具有以下簽名:
Task DoWorkAsync(CancellationToken cancellationToken);
你不得不改變(僅略,你也不會在這裏滲出關注點分離)的StartWork
方法考慮到傳遞給CreateNeverEndingTask
方法的新簽名,就像這樣:
void StartWork()
{
// Create the token source.
wtoken = new CancellationTokenSource();
// Set the task.
task = CreateNeverEndingTask((now, ct) => DoWorkAsync(ct), wtoken.Token);
// Start the task. Post the time.
task.Post(DateTimeOffset.Now, wtoken.Token);
}
任務似乎是一個矯枉過正考慮你想要達到的目標。 http://en.wikipedia.org/wiki/KISS_principle。在OnTick()開始時停止定時器,檢查一個bool,看看你是否應該做任何事情,做工作,當你完成後重新啓動Timer。 –