zoukankan      html  css  js  c++  java
  • .Net 并行编程系列之 Task取消

    在Task运行过程中,我们可以通过.Net 4中的内置方法来取消Task的运行。

    创建一个可取消的Task需要用到下面的一些对象:

    1.System.Threading.CancellationTokenSource实例

    CancellationTokenSource tokenSource = new CancellationTokenSource();

    2.通过CancellationTokenSource.Token属性获得一个取消令牌

    CancellationToken token = tokenSource.Token;

    3.创建Task对象,并且在构造函数传入Action(或者Action<T>)委托作为第一个参数,CancellationToken作为第二个参数(重要)

    Task task = new Task(() =>
    {
    // do something
    }, token);
    task.Start();

     4.创建Task对象也可以通过调用System.Threading.Tasks.TaskFactory 类提供的静态方法

    Task task = Task.Factory.StartNew(() =>
    {
        // do something ......
    }, token);

    如果想要取消Task的运行,除了要调用CancellationTokenSource实例的Cancel()方法之外,我们的Action委托中还需要检测CancellationToken的取消状态并编写相应代码(抛出异常)来阻止Task的运行。

    可以通过以下方式来检测Task取消状态:

    1.通过轮询的方式检测CancellationToken取消标记,该操作类似于轮询异步操作的IAsyncResult.IsCompleted状态,也是通过在循环中判断CancellationToken.IsCancellationRequested属性来检测Task是否被取消,如果为True则在Action委托中抛出异常来取消继续运行Task。 

    static void Main(string[] args)
    {
        CancellationTokenSource tokenSource = new CancellationTokenSource();
        CancellationToken token = tokenSource.Token;
        Task task = new Task(() =>
        {
            while (true)
            {
                if(token.IsCancellationRequested)
                {
                    // 释放资源操作等等...
                    throw new OperationCanceledException(token);
                }
                Console.Write(".");
                Thread.Sleep(100);
            }
        }, token);
    
        Console.WriteLine("Task is Running.");
        Console.WriteLine("Press anykey to cancel task.");    
    task.Start();
    Console.ReadKey(
    true); Console.WriteLine(); Console.WriteLine("Cancelling task."); tokenSource.Cancel();
    Console.WriteLine(
    "Main method complete."); Console.WriteLine("Press enter to finish."); Console.ReadLine(); }

    如果不需要释放系统资源,那么可以直接调用CancellationToken.ThrowIfCancellationRequested()方法,其实现如下:

    [__DynamicallyInvokable]
    public void ThrowIfCancellationRequested()
    {
        if (this.IsCancellationRequested)
        {
            this.ThrowOperationCanceledException();
        }
    }

     示例:

    while (true)
    {
        token.ThrowIfCancellationRequested();
        Console.Write(".");
        Thread.Sleep(100);
    }

    2.通过委托(Delegate)来检测Task是否取消,注册一个在取消CancellationToken时调用的委托,当CancellationTokenSource发送取消请求时,该委托即会运行,我们可以在委托方法中实现通知功能等等。

    static void Main(string[] args)
    {
        CancellationTokenSource tokenSource = new CancellationTokenSource();
        CancellationToken token = tokenSource.Token;
        Task task = new Task(() =>
        {
            while (true)
            {
                token.ThrowIfCancellationRequested();
                Console.Write(".");
                Thread.Sleep(100);
            }
        }, token);
    
        token.Register(() => { Console.WriteLine("The delegate is triggered."); });
    
        Console.WriteLine("Task is Running.");
        Console.WriteLine("Press anykey to cancel task.");
    
        task.Start();
    
        Console.ReadKey(true);
        Console.WriteLine();
        Console.WriteLine("Cancelling task.");
        tokenSource.Cancel();
    
        Console.WriteLine("Main method complete.");
        Console.WriteLine("Press enter to finish.");
        Console.ReadLine();
    }

    3.用WaitHandle来检测Task是否取消,当在CancellationToken.WaitHandle上调用WaitOne()方法时,会阻止当前线程执行,直到该CancellationToken接收到取消请求标记时,被token阻止的Task线程才会释放并继续执行。

    多个Task实例使用同一个CancellationToken时,当CancellationToken接收到取消请求标记时,所有在构造函数中使用该token实例化的Task都会被取消。WaitOne(int millisecondsTimeout)可以使用等待毫秒数作为参数,超过等待时间将会释放阻止的线程。

    不管WaitOne(int millisecondsTimeout)设置多长的等待时间,只要CancellationToken接收到取消请求标记时Task都会取消,而如果使用Thread.Sleep(100000)进行线程等待时,那么即使CancellationToken接收到取消请求标记,该Task也会等到Thread.Sleep执行完成才会Cancel。

    static void Main(string[] args)
    {
        CancellationTokenSource tokenSource = new CancellationTokenSource();
        CancellationToken token = tokenSource.Token;
    
        Task task = new Task(() =>
        {
            while (true)
            {                    
                token.ThrowIfCancellationRequested();
                Console.Write(".");
                Thread.Sleep(100);
            }
        }, token);
    
        Task task1 = new Task(() =>
        {
            token.WaitHandle.WaitOne();
            Console.WriteLine("WaitHandle released.");
        }, token);
    
        Console.WriteLine("Task is Running.");
        Console.WriteLine("Press anykey to cancel task.");
    
        task.Start();
        task1.Start();
    
        Console.ReadKey(true);
        Console.WriteLine();
        Console.WriteLine("Cancelling task.");
        tokenSource.Cancel();
    Console.WriteLine(
    "Main method complete."); Console.WriteLine("Press enter to finish."); Console.ReadLine(); }

    4.通过Task的IsCancelled属性来判断Task是否被取消,如果Task实例化时构造函数没有传入CancellationToken对象,则取消Task运行之后通过Task.IsCanceled属性获取到的值还是False而不是TrueTask.ContinueWith()方法如果没有传入CancellationToken对象,则Task即使是取消执行也会继续执行Task.ContinueWith()方法的Action委托,如果传入与Task相同的CancellationToken对象,则Task取消执行后Task.ContinueWith()方法中的Action委托也不会继续执行。

    {
        CancellationTokenSource tokenSource = new CancellationTokenSource();
        CancellationToken token = tokenSource.Token;
        Task task = new Task(() =>
        {
            try
            {
                Console.WriteLine("Task: Running");
                Thread.Sleep(5000);
                Console.WriteLine("Task: ThrowIfCancellationRequested");
                token.ThrowIfCancellationRequested();
                Thread.Sleep(2000);
                Console.WriteLine("Task: Completed");
            }
            catch (Exception exception)
            {
                Console.WriteLine("Task: " + exception.GetType().Name);
                throw;
            }
        }, token);
    
        task.ContinueWith(t => Console.WriteLine("ContinueWith: tokenSource.IsCancellationRequested = {0}, task.IsCanceled = {1}, task.Exception = {2}", tokenSource.IsCancellationRequested, t.IsCanceled, t.Exception == null ? "null" : t.Exception.GetType().Name));
        task.Start();
        
        Thread.Sleep(1000);
    
        Console.WriteLine("Main: Cancel");
        tokenSource.Cancel();
    
        try
        {
            Console.WriteLine("Main: Wait");
            task.Wait();
        }
        catch (Exception exception)
        {
            Console.WriteLine("Main: Catch " + exception.GetType().Name);
        }
    
        Console.WriteLine("Main: task.IsCanceled = {0}", task.IsCanceled);
        Console.WriteLine("Press any key to exit...");
    
        Console.ReadKey(true);
    }

    通过执行Task.WaitAll(task),Task.WaitAny(task),task.Result,task.Wait()出现了异常抛出的是一个System.AggregateException;

    通过执行task.Wait(CancellationToken)出现了异常抛出的是一个OperationCanceledException;

  • 相关阅读:
    C++小项目:directx11图形程序(七):modelclass
    C++小项目:directx11图形程序(六):cameraclass
    C++小项目:directx11图形程序(五):shadersclass
    C++小项目:directx11图形程序(四):d3dclass
    C++小项目:directx11图形程序(三):graphicsclass
    C++小项目:directx11图形程序(二):systemclass
    舞台上加载一外部SWF,舞台如何调用该swf内部方法呢?
    as3绘制抛物线
    Unity常用代码总结
    C# ref和out区别介绍
  • 原文地址:https://www.cnblogs.com/zhengzc/p/8508889.html
Copyright © 2011-2022 走看看