如何使用取消令牌在 C# 中取消被阻止的任务

本文关键字:取消 任务 何使用 令牌 | 更新日期: 2023-09-27 17:55:21

我有一个任务总是被阻止,我有一个CancelToken传递给它,用于取消任务。但是,永远不会执行设置为在任务取消时执行的继续任务。代码为:

    _tokenSrc = new CancellationTokenSource();
    var cnlToken = _tokenSrc.Token;
    Task.Run(() => 
          // _stream.StartStream() blocks forever  
          _stream.StartStream(), cnlToken)
        .ContinueWith(ant =>
        {
            _logger.Warn("Stream task cancellation requested, stopping the stream");
            _stream.StopStream();
            _stream = null;
            _logger.Warn("Stream stopped and task cancelled");
        }, TaskContinuationOptions.OnlyOnCanceled);

稍后在代码中的其他地方...

_tokenSrc.Cancel();

我不得不使用任务进行_stream的原因。StartStream() 是这个调用永远阻塞(一个我无法控制的 api,请注意,_stream指的是从 Web 服务流式传输数据的第三方 Api),所以我不得不在另一个线程上调用它。

取消任务的最佳方法是什么?

如何使用取消令牌在 C# 中取消被阻止的任务

[更新]

我将代码更改为以下代码,从而解决了问题:

Task.Run(() =>
{
    var innerTask = Task.Run(() => _stream.StartStream(), cToken);
    innerTask.Wait(cToken);
}, cToken)
.ContinueWith(ant =>
{
    _logger.Warn("Stream task cancellation requested, stopping the stream");
    _stream.StopStream();
    _stream = null;
    _logger.Warn("Stream stopped and task cancelled");
}, TaskContinuationOptions.OnlyOnCanceled);

您可以在CancellationToken上使用 Register 方法来注册将在请求取消时调用的委托。在委托中,调用取消阻止被阻止的操作的方法。

像这样:

_tokenSrc = new CancellationTokenSource();
var cnlToken = _tokenSrc.Token;
var task = Task.Factory.StartNew(() =>
{
    using(var tokenReg = cnlToken.Register(() => 
        {
            _logger.Warn("Stream task cancellation requested, stopping the stream");
            _stream.StopStream();
            _stream = null;
            _logger.Warn("Stream stopped and task cancelled");
        }))
    {
        _stream.StartStream(), cnlToken)
    }
}, cnlToken);

MSDN"如何:注册取消请求的回调"

如何使用取消令牌在这里清楚地描述:http://msdn.microsoft.com/en-us/library/dd997396(v=vs.110).aspx并建议遵循模式。

我将报告该示例,以防页面出现故障:

using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
    static void Main()
    {
        var tokenSource2 = new CancellationTokenSource();
        CancellationToken ct = tokenSource2.Token;
        var task = Task.Factory.StartNew(() =>
        {
            // Were we already canceled?
            ct.ThrowIfCancellationRequested();
            bool moreToDo = true;
            while (moreToDo)
            {
                // Poll on this property if you have to do 
                // other cleanup before throwing. 
                if (ct.IsCancellationRequested)
                {
                    // Clean up here, then...
                    ct.ThrowIfCancellationRequested();
                }
            }
        }, tokenSource2.Token); // Pass same token to StartNew.
        tokenSource2.Cancel();
        // Just continue on this thread, or Wait/WaitAll with try-catch: 
        try
        {
            task.Wait();
        }
        catch (AggregateException e)
        {
            foreach (var v in e.InnerExceptions)
                Console.WriteLine(e.Message + " " + v.Message);
        }
        Console.ReadKey();
    }
}

这里有一个更深入的示例:http://msdn.microsoft.com/en-us/library/dd537607(v=vs.110).aspx但第一个应该足以满足你的方案。