當前位置:首頁 > 教育 > 正文

實施永無止境的任務的正确方法 (計時器與任務)

我将為此使用TPL Dataflow(因為您使用的是.NET 4.5,并且它在Task内部使用)。您可以輕松地創建一個ActionBlock在處理完操作并等待适當時間後将項目發布到其自身的對象。

首先,創建一個工廠,該工廠将創建您永無止境的任務:

ITargetBlock CreateNeverEndingTask(
    Action action, CancellationToken cancellationToken)
{
    // Validate parameters.
    if (action == null) throw new ArgumentNullException("action");

    // Declare the block variable, it needs to be captured.
    ActionBlock 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(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一個DateTimeOffset結構 ; 您必須傳遞一個類型參數,它也可能傳遞一些有用的狀态(可以根據需要更改狀态的性質)。

另外,請注意,ActionBlock默認情況下,一次隻能處理 一項 ,因此可以确保隻處理一項操作(這意味着,當它再次調用擴展方法時,您不必處理重入)。Post

我還将該CancellationToken結構傳遞給的構造函數ActionBlockTask.Delay方法調用;如果取消了該過程,則取消将在第一個可能的機會發生。

從那裡,可以輕松地重構代碼來存儲實現的ITargetBlock接口ActionBlock(這是代表作為使用者的塊的高級抽象,并且您希望能夠通過調用Post擴展方法來觸發使用):

CancellationTokenSource wtoken;
ActionBlock 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來處理您的操作以及對的調用Task.Delay,您可以在實際上不執行任何操作的情況下控制所使用的線程。當然,當您生成新的Task将繼續處理的代碼時,這實際上會導緻一些開銷,但是考慮到您不是在緊密的循環中進行處理(每次調用之間要等待十秒鐘),所以這應該很小。

如果DoWork實際上可以使該函數處于等待狀态(即,該函數返回a Task),那麼您可以(可能)通過調整上面的factory方法采用aFunc而不是來優化此效果Action,如下所示:

ITargetBlock CreateNeverEndingTask(
    Func action, 
    CancellationToken cancellationToken)
{
    // Validate parameters.
    if (action == null) throw new ArgumentNullException("action");

    // Declare the block variable, it needs to be captured.
    ActionBlock 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(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()取消它?

總結

以上是真正的電腦專家為你收集整理的實施永無止境的任務的正确方法。(計時器與任務)的全部内容,希望文章能夠幫你解決所遇到的問題。

如果覺得真正的電腦專家網站内容還不錯,歡迎将真正的電腦專家推薦給好友。

你可能想看:

有話要說...

取消
掃碼支持 支付碼