重入模式5:取消先前的调用

时间:2017-09-05 06:56:01

标签: c# task cancellationtokensource .net-4.6.2

我需要重新进入任务,并将类似的代码实现到https://blogs.msdn.microsoft.com/lucian/2014/03/03/async-re-entrancy-and-the-patterns-to-deal-with-it/(模式5)

但是我想知道CancellationTokenSource配置是否没有丢失。我的实现是将它添加到.ContinueWith

    private Task _fooAsyncTask;
    private CancellationTokenSource _fooAsyncCancellation;

    public async Task Button1Click()
    {
        // Assume we're being called on UI thread... if not, the two assignments must be made atomic.
        // Note: we factor out "FooHelperAsync" to avoid an await between the two assignments. 
        // without an intervening await. 
        _fooAsyncCancellation?.Cancel();
        _fooAsyncCancellation = new CancellationTokenSource();
        _fooAsyncTask = FooHelperAsync(_fooAsyncCancellation.Token);

        await _fooAsyncTask.ContinueWith(task =>
        {
            _fooAsyncCancellation.Dispose();
            _fooAsyncCancellation = null;
        });
    }

    private async Task FooHelperAsync(CancellationToken cancel)
    {
        try { if (_fooAsyncTask != null) await _fooAsyncTask; }
        catch (OperationCanceledException) { }
        cancel.ThrowIfCancellationRequested();
        await FooAsync(cancel);
    }

    private async Task FooAsync(CancellationToken cancel)
    {
        //
    }

这是对的吗?

1 个答案:

答案 0 :(得分:0)

您应该将代码更改为

  • 在UI线程上将_ fooAsyncCancellation设置为null(续接时不是)
  • 保证,您处置为那个任务创建的CTS。

这里是修改后的代码

Task _fooAsyncTask;
CancellationTokenSource _fooAsyncCancellation;

async void button1_Click(object sender, EventArgs e)
{
    _fooAsyncCancellation?.Cancel();
    using (var cts = new CancellationTokenSource())
    {
        _fooAsyncCancellation = cts;

        try
        {
            await FooAsyncHelper(cts.Token);
        }
        catch (OperationCanceledException) { }

        if (_fooAsyncCancellation == cts)
        {
            _fooAsyncCancellation = null;
        }
    }
}

async Task FooAsyncHelper(CancellationToken cancellationToken)
{
    try
    {
        if (_fooAsyncTask != null)
        {
            await _fooAsyncTask;
        }
    }
    catch (OperationCanceledException) { }
    cancellationToken.ThrowIfCancellationRequested();
    await FooAsync(cancellationToken);
}

async Task FooAsync(CancellationToken cancellationToken)
{
    // just some sample work to do
    for (int i = 0; i < 100; i++)
    {
        await Task.Delay(100);
        cancellationToken.ThrowIfCancellationRequested();
    }
}