您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

实施永无止境的任务的正确方法。(计时器与任务)

实施永无止境的任务的正确方法。(计时器与任务)

我将为此使用TPL Dataflow(因为您使用的是.NET 4.5,并且它在Task内部使用)。您可以轻松地创建一个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);
}
其他 2022/1/1 18:17:34 有653人围观

撰写回答


你尚未登录,登录后可以

和开发者交流问题的细节

关注并接收问题和回答的更新提醒

参与内容的编辑和改进,让解决方法与时俱进

请先登录

推荐问题


联系我
置顶