欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

取消Thread,取消Task, CancellationTokenSource

程序员文章站 2022-07-12 19:39:28
...

1、从 .NET Framework 4 开始,.NET Framework 在协作取消异步操作或长时间运行的同步操作时使用统一的模型。 此模型基于被称为取消标记(CancellationTokenSource)的轻量对象。 调用一个或多个可取消操作的对象(例如通过创建新线程(Thread)或任务(Task))将标记传递给每个操作。

2、用于实现协作取消模型的常规模式是:

3、在以下示例中,请求对象创建 CancellationTokenSource 对象,然后传递其 Token 属性到可取消操作中。 接收请求的操作通过轮询监视标记的 IsCancellationRequested 属性的值。 值变为 true 后,侦听器可以适当方式终止操作。 在此示例中,方法只需退出,很多情况下都只需执行此操作。

using System;
using System.Threading;

public class Example
{
   public static void Main()
   {
      // 创建取消对象实例.
      CancellationTokenSource cts = new CancellationTokenSource();

      // 传递token到取消操作.
      ThreadPool.QueueUserWorkItem(new WaitCallback(DoSomeWork), cts.Token);
      Thread.Sleep(2500);

      // 发送取消操作
      cts.Cancel();
      Console.WriteLine("Cancellation set in token source...");
      Thread.Sleep(2500);
      //取消对象最后要释放。
      cts.Dispose();
   }

   // Thread 2: The listener
   static void DoSomeWork(object obj)
   {
      CancellationToken token = (CancellationToken)obj;

      for (int i = 0; i < 100000; i++) {
         if (token.IsCancellationRequested)
         {
            Console.WriteLine("In iteration {0}, cancellation has been requested...",
                              i + 1);
            // Perform cleanup if necessary.
            //...
            // Terminate the operation.
            break;
         }
         // Simulate some work.
         Thread.SpinWait(500000);
      }
   }
}
// The example displays output like the following:
//       Cancellation set in token source...
//       In iteration 1430, cancellation has been requested...

 Note! 在标记的 IsCancellationRequested 属性设置为 true 后,不能重置为 false 因此,取消后不能重用取消标记。

4、 某些操作可能被阻止,导致其无法及时检查取消标记的值。 对于这些情况,可以注册在接收取消请求时取消阻止此方法的回调方法。

 

Register 方法返回专用于此目的的 CancellationTokenRegistration 对象。此方法可以注册一个取消请求发出后执行的一个操作。 以下示例演示了如何使用 Register 方法取消异步 Web 请求。

using System;
using System.Net;
using System.Threading;

class Example
{
    static void Main()
    {
        CancellationTokenSource cts = new CancellationTokenSource();

        StartWebRequest(cts.Token);

        // cancellation will cause the web 
        // request to be cancelled
        cts.Cancel();
    }

    static void StartWebRequest(CancellationToken token)
    {
        WebClient wc = new WebClient();
        wc.DownloadStringCompleted += (s, e) => Console.WriteLine("Request completed.");

        // Cancellation on the token will 
        // call CancelAsync on the WebClient.
        token.Register(() =>
        {
            wc.CancelAsync();
            Console.WriteLine("Request cancelled!");
        });

        Console.WriteLine("Starting request.");
        wc.DownloadStringAsync(new Uri("http://www.contoso.com"));
    }
}

 5、在更复杂的情况下,用户委托可能需要通知库代码已发生取消。 在这种情况下,终止操作的正确方式是委托调用ThrowIfCancellationRequested 方法,这将引发 OperationCanceledException 库代码可以在用户委托线程上捕获此异常,并检查异常的标记以确定异常是否表示协作取消或一些其他的异常情况。

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);
        }
        finally
        {
            tokenSource2.Dispose();
        }

        Console.ReadKey();
    }
}