我会尽力为您进行翻译。这段内容是关于编程的,建议使用
TPL Dataflow(因为您正在使用.NET 4.5,并且它在内部使用
Task
)。您可以轻松地创建一个
ActionBlock<TInput>
,在处理完其操作并等待适当的时间后,将项目发布到自身。
首先,创建一个工厂来创建您的无休止任务:
ITargetBlock<DateTimeOffset> CreateNeverEndingTask(
Action<DateTimeOffset> action, CancellationToken cancellationToken)
{
if (action == null) throw new ArgumentNullException("action");
ActionBlock<DateTimeOffset> block = null;
block = new ActionBlock<DateTimeOffset>(async now => {
action(now);
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken).
ConfigureAwait(false);
block.Post(DateTimeOffset.Now);
}, new ExecutionDataflowBlockOptions {
CancellationToken = cancellationToken
});
return block;
}
I've chosen the
ActionBlock<TInput>
to take a
DateTimeOffset
structure; you have to pass a type parameter, and it might as well pass some useful state (you can change the nature of the state, if you want).
Also, note that the
ActionBlock<TInput>
by default processes only
one item at a time, so you're guaranteed that only one action will be processed (meaning, you won't have to deal with
reentrancy when it calls the
Post
extension method back on itself).
I've also passed the
CancellationToken
结构 to both the constructor of the
ActionBlock<TInput>
and to the
Task.Delay
方法 call; if the process is cancelled, the cancellation will take place at the first possible opportunity.
From there, it's an easy refactoring of your code to store the
ITargetBlock<DateTimeoffset>
接口 implemented by
ActionBlock<TInput>
(this is the higher-level abstraction representing blocks that are consumers, and you want to be able to trigger the consumption through a call to the
Post
extension method):
CancellationTokenSource wtoken;
ActionBlock<DateTimeOffset> task;
你的StartWork
方法:
void StartWork()
{
wtoken = new CancellationTokenSource();
task = CreateNeverEndingTask(now => DoWork(), wtoken.Token);
task.Post(DateTimeOffset.Now);
}
接下来是你的StopWork
方法:
void StopWork()
{
using (wtoken)
{
wtoken.Cancel();
}
wtoken = null;
task = null;
}
为什么你想在这里使用TPL数据流?有几个原因:
关注点分离
CreateNeverEndingTask
方法现在是一个工厂,创建你的“服务”。你控制它何时启动和停止,它是完全自包含的。你不需要将定时器的状态控制与代码的其他方面交织在一起。你只需创建该块,启动它,并在完成后停止它。
更有效地使用线程/任务/资源
TPL数据流块的默认调度程序与Task
相同,即线程池。通过使用ActionBlock<TInput>
来处理你的操作,以及调用Task.Delay
,当你实际上没有做任何事情时,你正在让你使用的线程放弃控制权。当然,当你生成处理连续体的新Task
时,这实际上会导致一些开销,但考虑到你不在紧密循环中处理它(你在调用之间等待十秒钟),这应该是很小的。
如果DoWork
函数实际上可以被设置为可等待的(也就是说,它返回一个Task
),那么你可能可以通过调整上面的工厂方法,使其接受Func<DateTimeOffset, CancellationToken, Task>
而不是Action<DateTimeOffset>
来进一步优化它,如下所示:
ITargetBlock<DateTimeOffset> CreateNeverEndingTask(
Func<DateTimeOffset, CancellationToken, Task> action,
CancellationToken cancellationToken)
{
if (action == null) throw new ArgumentNullException("action");
ActionBlock<DateTimeOffset> block = null;
block = new ActionBlock<DateTimeOffset>(async now => {
await action(now, cancellationToken).
ConfigureAwait(false);
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken).
ConfigureAwait(false);
block.Post(DateTimeOffset.Now);
}, new ExecutionDataflowBlockOptions {
CancellationToken = cancellationToken
});
return block;
}
当然,将CancellationToken传递到您的方法(如果它接受一个)是一个好的实践方法,在这里完成该操作。
这意味着您将有一个
DoWorkAsync
方法,其签名如下:
Task DoWorkAsync(CancellationToken cancellationToken);
您需要略微修改(不用过分关注关注责任分离)StartWork
方法以适应传递给CreateNeverEndingTask
方法的新签名,如下所示:
void StartWork()
{
wtoken = new CancellationTokenSource();
task = CreateNeverEndingTask((now, ct) => DoWorkAsync(ct), wtoken.Token);
task.Post(DateTimeOffset.Now, wtoken.Token);
}