如何解决实施永无止境的任务的正确方法计时器与任务
我将为此使用TPL
Dataflow(因为您使用的是.NET
4.5,并且它在@L_502_1@内部使用)。您可以轻松地创建一个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
结构 ;
您必须传递一个类型参数,它也可能传递一些有用的状态(可以根据需要更改状态的性质)。
另外,请注意,ActionBlock<TInput>
默认情况下,一次只能处理 一项
,因此可以确保只处理一项操作(这意味着,当它再次调用扩展方法时,您不必处理重入)。Post
我还将该CancellationToken
结构传递给的构造函数ActionBlock<TInput>
和Task.Delay
方法调用;如果取消了该过程,则取消将在第一个可能的机会发生。
从那里,可以轻松地重构代码来存储实现的ITargetBlock<DateTimeoffset>
接口ActionBlock<TInput>
(这是代表作为使用者的块的高级抽象,并且您希望能够通过调用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
现在,该方法是一家工厂,可以创建您的“服务”。您可以控制它的启动和停止时间,它是完全独立的。您不必将计时器的状态控制与代码的其他方面交织在一起。您只需创建块,然后启动它,然后在完成时停止它即可。
对于Task
线程池,TPL数据流中块的默认调度程序是相同的。通过使用ActionBlock<TInput>
来处理您的操作以及对的调用Task.Delay
,您可以在实际上不执行任何操作的情况下控制所使用的线程。当然,当您生成新的Task
将继续处理的代码时,这实际上会导致一些开销,但是考虑到您不是在紧密的循环中进行处理(每次调用之间要等待十秒钟),所以这应该很小。
如果DoWork
实际上可以使该函数处于等待状态(即,该函数返回a Task
),那么您可以(可能)通过调整上面的factory方法采用aFunc<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);
}
解决方法
因此,只要应用程序正在运行或要求取消,我的应用程序就需要几乎连续地执行操作(每次运行之间要暂停10秒左右)。它需要做的工作可能要花费30秒。
最好使用System.Timers.Timer并使用AutoReset来确保它在上一个“刻度”完成之前没有执行操作。
还是应该在带有取消令牌的LongRunning模式下使用常规Task,并在其中执行常规的while循环,以调用之间用10秒的Thread.Sleep进行操作?至于异步/等待模型,我不确定这里是否合适,因为我没有任何返回值。
CancellationTokenSource wtoken;
Task task;
void StopWork()
{
wtoken.Cancel();
try
{
task.Wait();
} catch(AggregateException) { }
}
void StartWork()
{
wtoken = new CancellationTokenSource();
task = Task.Factory.StartNew(() =>
{
while (true)
{
wtoken.Token.ThrowIfCancellationRequested();
DoWork();
Thread.Sleep(10000);
}
},wtoken,TaskCreationOptions.LongRunning);
}
void DoWork()
{
// Some work that takes up to 30 seconds but isn't returning anything.
}
或者只是在使用其AutoReset属性时使用简单的计时器,然后调用.Stop()取消它?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。