【4858.com】的变迁史,编制程序类别之叁

By admin in 4858.com on 2019年5月7日

在Task运营进程中,大家能够通过.Net 四中的内置方法来打消Task的周转。

.NET 4 彼此(多核)编制程序体系之三 从Task的吊销

认知非同步程序开拓设计模型

4858.com 1

  在.NET
肆.0中,并行总括与102线程得到了迟早程度的增加,那根本反映在相互对象Parallel,三十二线程Task,与PLinq。这里对那个相关的性状一齐总结一下。

创设3个可撤废的Task需求运用上面包车型地铁局地目的:

 

从VS二〇一二起来引进的新的非同步程序设计的补助——-async/await设计模型

  1. 在此以前的当大家帮衬非同步作业的时候,往往接纳二10四线程开解决,大家相比较熟练的正是

  2. 实行者:Thread,ThreadPool
    (线程和线程池,后者有利于财富的管事运用)

  3. 非同步的宏图模型:Begin方法/End方法,Async事件/Completed事件(主借使异步委托之类的,笔者在自己原先的博文中有写过专题)

  4. BackgroundWorker控制项

  5. Task Parallel
    Library

style=”font-family: 燕体; font-size: 1八px;”>  固然今日的要害是.NET四.伍的async/await设计情势不过由于过五人对于.NET④.0中的Task如故依旧尚未接触过,Task也是.NET
四.5 async
await的根底概念之一,值得我们花点时间熟练,那么这里就将他也作为1个外加专题来做一下执教。

  使用Thread情势的线程无疑是比较劳碌的,于是在那些本子中有了考订的本子Task。除了运营成效等方面包车型大巴晋升,Task还与并行计算牢牢关系在了联合,这一个线程丰硕的选择了多核的优势,在分明的地方下,小幅度的增高了先后的运行功用。屏蔽了运营细节的Task和Parallel情势使得程序员们一心不用编写任何针对多核的主次,只必要使用正式的类库完结职责就足以了,别的的CLBMWX三会去处理。

1.System.Threading.CancellationTokenSource实例

  前言:因为Task是.NET 四并行编制程序最为基本的二个类,也咱们在是在交互编制程序平时打交道的类,所以,对Task对健全的询问很有必不可缺。

专属专题:.NET四.0十二线程开拓之利器—àTask

到大家在开采Signal中华V程序的时候,就必须求使用到二十多线程,假设未有.NET四.5的帮助,那么您或者想到的最轻易易行方法正是行使Task,它代替了守旧的Thread,TheadPool的写法,能大幅的简化同步逻辑的写法,颇为有益,上边大家来看多少个标准的范例。

那一篇中先看率先个利器:二拾多线程Task。

CancellationTokenSource tokenSource = new CancellationTokenSource();

  上篇小说主要讲述了怎么创制一个task,本篇小说首要描述怎么样撤消3个task。

典范1:简单的始发

Test一()用以另一Thread实践Thread.Sleep()及Console.WriteLine(),效果与ThreadPool.QueueUserWorkItem()万分。

  private static void Test1()
        {
            //Task可以代替TheadPool.QueueUserWorkItem使用
            Task.Factory.StartNew(() =>
                {
                    Thread.Sleep(2000);
                    Console.WriteLine("Done!");
                });
            Console.WriteLine("Async Run...");
        }

StartNew()完会立时施行下壹行,故会先看看Aync
Run,一秒后打字与印刷出Done。

 

  Task类为把线程类实行改良,使之使用起来更便捷,越发便于。要想张开二个新的线程实施职务,只要调用Task.Factory.StartNew方法就足以了,实施完那一个讲话后线程就发轫运营了。当然了,使用new起首化二个Task,然后适时调用其Start方法伊始运营也是很不利的一个摘取。

2.由此CancellationTokenSource.Token属性获得多个撤除令牌

 

典范二:等待各作业成功再持续下一步的应用场境

 

  同时开动八个作业多工并行(三四线程并行),但要等待各作业成功再持续下一步的应用场境古板办法上可经过WaitHandle、AutoReset伊夫nt、马努alReset伊夫nt等体制完成;Task的写法十分简单,创设多個Task对象,再作为Task.WaitAny()或Task.WaitAll()的参数就解决了! 

private static void Test2()
        {
            var task1 = Task.Factory.StartNew(() =>
            {
                Thread.Sleep(3000);
                Console.WriteLine("Done!(3s)");
            });
            var task2 = Task.Factory.StartNew(() =>
            {
                Thread.Sleep(5000);
                Console.WriteLine("Done!(5s)");
            });
            //等待任意作业完成后继续
            Task.WaitAny(task1, task1);
            Console.WriteLine("WaitAny Passed");
            //等待所有作业完成后继续
            Task.WaitAll(task1, task2);
            Console.WriteLine("WaitAll Passed");
        }

 

task一耗费时间3秒、task二耗费时间伍秒,所以3秒后WaitAny()实行到位、五秒后WaitAll()施行完结。

using System;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {    
        var task = Task.Factory.StartNew(() =>
        {
           for (int i = 0; i < 100; i++) { Console.Write('B'); }
        });

        task.ContinueWith(t =>
        {
            Console.WriteLine();
            Console.WriteLine("sub task {0} done", t.Id);
        });

        for (int i = 0; i < 100; i++) { Console.Write('A'); }

        task.Wait();
    }
}
CancellationToken token = tokenSource.Token;

  本篇主的要紧议题如下:

圭臬叁:借使要等待多工作业传回结果

透过StartNew<T>()钦定传回类型建设构造作业,随后以Task.Result取值,不用额外Code就能够保险多专门的职业业实践到位后才读取结果一而再运维

private static void Test3()
        {
            var task = Task.Factory.StartNew<string>(() =>
            {
                Thread.Sleep(2000);
                return "Done!";
            });
            //使用秒表计时
            Stopwatch sw = new Stopwatch();
            sw.Start();
            //读取task.Result时,会等到作业完成传回值后才继续
            Console.WriteLine("{0}", task.Result);
            sw.Stop();
            //取得task.Result耗时约2秒
            Console.WriteLine("Duration: {0:N0}ms", sw.ElapsedMilliseconds);
        }

实则施行,要花两秒才干跑完Console.WriteLine(“{0}”,
task.Result),其长度正是Task執行并回传结果的年华。

  注意最终的task.Wait(),调用这些格局是等待子线程实施实现,当须求等待子线程结果的时候,它最有用。

3.开立Task对象,并且在构造函数字传送入Action(或者Action<T>)信托作为第一个参数,CancellationToken用作第三个参数(重要)

1.      
  一. 经过轮询的不2诀要检验Task是还是不是被注销

范例4:在多工作业完毕后接二连三运转行另一段程序可使用ContinueWith():

   private static void Test4()
        {
            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(1000);
                Console.WriteLine("Done!");
            }).ContinueWith(task =>
            {
                //ContinueWith会等待前面的任务完成才继续
                Console.WriteLine("In ContinueWith");
            });
            Console.WriteLine("Async Run...");
        }

如预期,ContinueWith()里的程序会在Task完成后才被执行。

  task对象还有诸多一蹴而就的措施,从它们的名字就足以知道它们各自的用处了,在那之中实例方法如上边的ContinueWith方法,它会在task试行完成后实行其参数钦点的一举一动;静态方法如WaitAny,WaitAll等,它们钦赐了在实践多少个task时主线程的等待条件。

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

二.     
  二. 
用委托delegate来检查评定Task是或不是被吊销

表率5:多做工作时各段逻辑便会依顺序推行

.ContinueWith()传回值仍是Task对象,所以大家可以跟jQuery同样连连看,在ContinueWith()後方再接上另二个ContinueWith(),各段逻辑便会依顺序实践。

static void test5()
        {
            //ContinueWith()可以串接
              Task.Factory.StartNew(() =>
            {
                Thread.Sleep(2000);
                Console.WriteLine("{0:mm:ss}-Done", DateTime.Now);
            })
            .ContinueWith(task =>
            {
                Console.WriteLine("{0:mm:ss}-ContinueWith 1", DateTime.Now);
                Thread.Sleep(2000);
            })
            .ContinueWith(task =>
            {
                Console.WriteLine("{0:mm:ss}-ContinueWith 2", DateTime.Now);
            });
            Console.WriteLine("{0:mm:ss}-Async Run...", DateTime.Now);
        }

Task耗费时间两秒,第壹個ContinueWith()耗時二秒,最终一个ContinueWith()继续在肆秒后实行。

  对于十2线程编制程序来讲,运维线程并等候其本来终止是最广泛的1种接纳,管理也相比较简单。相比来讲,线程的中途截止和那贰个的拍卖要麻烦的多,难以预测的隐藏bug会产出在线程程序中。

 四.开立Task对象也得以通过调用System.Threading.Tasks.TaskFactory.aspx) 类提供的静态方法

三.     
  
三.
用Wait
Handle还检查实验Task是不是被打消

模范6:Task有监察和控制状态的体制

ContinueWith()中的Action<Task>都会有二个输入参数,用于以获知前一Task的实行情形,有IsCompleted,
IsCanceled,
IsFaulted多少个属性可用。要撤消推行,得依靠CancellationTokenSource及其所属CancellationToken类型,做法是在Task中不停呼叫CancellationToken.ThrowIfCancellationRequested(),一旦外部呼叫CancellationTokenSource.Cancel(),便会触发OperationCanceledException,Task有监督此拾1分现象的建制,将终结课业试行后续ContinueWith(),并点名Task.IsCanceled为True;而当Task程序发送Exception,也会终结触发ContinueWith
(),此時Task.IsFaulted为True,ContinueWith()中可通过Task.Exception.InnerExceptions获得错误细节。以下顺序同时可测试Task经常、撤除及错误三种情形,使用者通过输入一,2或三来控制要测试哪一种。在Task外先声爱他美(Aptamil)个CancellationTokenSource类型,将个中的Token属性当成StartNew()的第一项参数,而Task中則保留最初的伍秒能够收回,方法是每隔一秒呼叫一回CancellationToken.ThrowIfCancellationRequested(),当程序外部调用CancellationTokenSource.Cancel(),Task就能够結束。五秒后若未收回,再依使用者决定的测试情境return结果也许抛出Exception。ContinueWith()则会检讨IsCanceled,
IsFaulted等标记,并出口结果。

  private static void Test6()
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            CancellationToken cancelToken = cts.Token;//获取与此CancellationTokenSource关联的CancellationToken
            Console.Write("Test Option 1, 2 or 3 (1-Complete / 2-Cancel / 3-Fault) : ");
            var key = Console.ReadKey();
            Console.WriteLine();
            Task.Factory.StartNew<string>(() =>
            {
                //保留5秒检测是否要Cancel
                for (var i = 0; i < 5; i++)
                {
                    Thread.Sleep(1000);
                    //如cancelToken.IsCancellationRequested
                    //引发OperationCanceledException
                    cancelToken.ThrowIfCancellationRequested();
                }
                switch (key.Key)
                {
                    case ConsoleKey.D1: //选1时
                        return "OK";
                    case ConsoleKey.D3: //选3时
                        throw new ApplicationException("MyFaultException");
                }
                return "Unknown Input";
            }, cancelToken).ContinueWith(task =>
            {
                Console.WriteLine("IsCompleted: {0} IsCanceled: {1} IsFaulted: {2}", task.IsCompleted, task.IsCanceled, task.IsFaulted);
                if (task.IsCanceled)
                {
                    Console.WriteLine("Canceled!");
                }
                else if (task.IsFaulted)
                {
                    Console.WriteLine("Faulted!");
                    foreach (Exception e in task.Exception.InnerExceptions)
                    {
                        Console.WriteLine("Error: {0}", e.Message);
                    }
                }
                else if (task.IsCompleted)
                {
                    Console.WriteLine("Completed! Result={0}", task.Result);
                }
            });
            Console.WriteLine("Async Run...");
            //如果要测Cancel,2秒后触发CancellationTokenSource.Cancel
            if (key.Key == ConsoleKey.D2)
            {
                Thread.Sleep(2000);
                cts.Cancel();
            }
        }

style=”font-family: 小篆; font-size: 1八px;”>Task能做的事,过去利用Thread/ThreadPool合作伊夫nt、WaitHandle同样能源办公室到,但利用Task能以比极短小的语法完结同样专业,使用.NET
四.0支出四线程时可多加运用。

到此处,大家后续回到原先的.NET四.5中,首先我们安顿三种异步作业新旧写法法举办对照

线程的停下类型

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

肆.      
  四. 打消四个Task

采取WebClient连串下载网页内容

  线程实践的职责实现之后,线程就见惯不惊截至了。这里线程职分达成常常有3种状态:职务正常试行完,职分被注销,任务被充裕打断甘休。查询Task截止时的情形类型正是调用相关的习性,如下边包车型大巴例子:

 

伍.      
  5. 开立组合的吊销Task的Token

一.施用Async/Complete设计情势

 private static void DownLoadWebPageSourceCode_Old()
        {
            WebClient wc = new WebClient();
            wc.DownloadStringCompleted += CompletedHandler;
            wc.DownloadStringAsync(new Uri("http://www.cnblogs.com/rohelm"));
            while (wc.IsBusy)
            {
                Console.WriteLine("还没下完,我喝一回茶!");
            }
        }

        private static void CompletedHandler(object sender, DownloadStringCompletedEventArgs e)
        {
            Console.WriteLine(e.Result);
        }

运作效果如下:

4858.com 2

static void Main(string[] args)
{
    Task t = new Task(() =>
    {
       Console.WriteLine("任务开始工作……");
       //模拟工作过程
       Thread.Sleep(5000);
    });
    t.Start();
    t.ContinueWith((task) =>
    {
       Console.WriteLine("任务完成,完成时候的状态为:");
       Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted);
    });
    Console.ReadKey();
}

假诺想要裁撤Task的运作,除了要调用CancellationTokenSource实例的Cancel()格局之外,大家的Action委托中还需求检查实验CancellationToken的撤除状态并编辑相应代码(抛出卓殊)来阻止Task的运行。

陆.      
  6.
判定2个Task是还是不是早已被撤除了

二.应用新的async/await设计方式

 private static async void DownLoadWebPageSourceCode_New()
        {
            WebClient wc = new WebClient();
            Console.WriteLine(await wc.DownloadStringTaskAsync("http://www.cnblogs.com/rohelm"));
        }

而它的个中贯彻机制实际上是大家前边的增大专题中关系的Task,我们来查阅下这么些艺术的源码:

[ComVisible(false), HostProtection(SecurityAction.LinkDemand, ExternalThreading=true)]
public Task<string> DownloadStringTaskAsync(string address)
{
    return this.DownloadStringTaskAsync(this.GetUri(address));
}

[ComVisible(false), HostProtection(SecurityAction.LinkDemand, ExternalThreading=true)]
public Task<string> DownloadStringTaskAsync(Uri address)
{
    TaskCompletionSource<string> tcs = new TaskCompletionSource<string>(address);
    DownloadStringCompletedEventHandler handler = null;
    handler = delegate (object sender, DownloadStringCompletedEventArgs e) {
        this.HandleCompletion<DownloadStringCompletedEventArgs, DownloadStringCompletedEventHandler, string>(tcs, e, args => args.Result, handler, delegate (WebClient webClient, DownloadStringCompletedEventHandler completion) {
            webClient.DownloadStringCompleted -= completion;
        });
    };
    this.DownloadStringCompleted += handler;
    try
    {
        this.DownloadStringAsync(address, tcs);
    }
    catch
    {
        this.DownloadStringCompleted -= handler;
        throw;
    }
    return tcs.Task;
}

  Task对象的多个属性IsCompleted,IsCanceled,IsFaulted正是查询线程的实施境况,但是必要留意,只要线程甘休了,不管是以什么样点子,IsCompleted始终再次来到true。IsCanceled代表先后被主动撤除了,IsFaulted代表线程出现相当被动截止,那三个值都为false,代表线程是常规施行完截至的。

能够通过以下办法来检查实验Task打消状态:

一.因此轮询的办法检验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【4858.com】的变迁史,编制程序类别之叁。对象,则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)出现了万分抛出的是3个OperationCanceledException;

 

3.撤废非同步的不二等秘书技

 
由于地点我们早就说过它的内部本质照旧Task所以它的,打消该非同步作业照旧注重CancellationTokenSource及其所属CancellationToken类型

private static async Task TryTask()
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            cts.CancelAfter(TimeSpan.FromSeconds(1));
            Task<string> task = Task.Run(() => PirntWords("Hello,Wrold!", cts.Token), cts.Token);
            Console.WriteLine(task.Result);
            await task;
        }

        private static string PirntWords(string input, CancellationToken token)
        {
            for (int i = 0; i < 20000000; i++)
            {
                Console.WriteLine(input);
                token.ThrowIfCancellationRequested();
            }
            return input;
        }

  符合规律甘休的图景比较轻易,这里就不多说了,这里看一下线程职务的吊销难点。那在编制程序中是很广泛的四个须求,运维了多少个线程以后,发掘一些规则有所了,就没有须求线程继续运转了,那年就要求撤销线程职责。

 

网页调用用八个Web服务/WCF服务/Http服务模型

       public async Task<ActionResult> DoAsync()
        {
            ServiceClient1 client1 = new ServiceClient1();
            ServiceClient2 client2 = new ServiceClient2();
            var task1 = client1.GetDataAsync();
            var task2 = client2.GetDataAsync();
            await Task.WhenAll(task1,task2);
            return View("View名称",new DataModel(task1.Result,task2.Rusult));
        }

 

是还是不是意识至极的造福,实用啊!

style=”font-family: 仿宋; font-size: 14pt;”>未完待续….

style=”font-family: 甲骨文; font-size: 1四pt;”>  后续内容:

style=”font-family: 钟鼓文; font-size: 14pt;”>   对WebAPI和WCF的实行二个粗略相比较,钻探WebAPI的体制,成效,架构,WinFrom
Client/WebService Client大数量上传… style=”font-family: 燕体; font-size: 1四pt;”>备注:本小说版权的尚未,归.NET使用者共有。

主动撤除/中止线程的标准做法

  连串文章链接:

  在在此以前的版本中,我基本上是都通Thread的Abort方法强行的间歇线程。在C#
四.0中,标准的打消一个线程任务的做法是行使合营式撤除(Cooperative
Cancellation)。合营式裁撤的机制是,假使线程须要被终止,那么线程本身就得承担开放给调用者那样的接口:Cancled,然后线程在办事的还要,不断以某种频率检查测试Cancled标志(平时是把任务重视包装到循环中),若检验到Cancled,线程本人担任退出。

  .NET 4互相(多核)编制程序系列之一入门介绍

下边是三个最基础的配合式撤废的样例:

  .NET 四 并行(多核)编制程序体系之②从Task起首 

// 设定取消标识
CancellationTokenSource cts = new CancellationTokenSource();
Thread t = new Thread(() =>
   {
       while (true)
       {
      // 检查取消标识
          if (cts.Token.IsCancellationRequested)
          {
              Console.WriteLine("线程被终止!");
              break;
          }
          Console.WriteLine(DateTime.Now.ToString());
          Thread.Sleep(1000);
        }
   });

t.Start();
Console.ReadLine();
// 主线程申请取消
cts.Cancel();

  .NET 四 交互(多核)编制程序体系之叁从Task的打消 

  调用者使用CancellationTokenSource的Cancle方法文告专门的学问线程退出。专业线程则以一定的的成效壹边干活,一边防检查查是或不是有外界流传进来的Cancel时域信号。若有这么的频域信号,则肩负退出。能够看看,在正确甘休线程的机制中,真正起到关键意义的是线程本身,它担当检验相关随机信号并脱离。

  .NET 四 互动(多核)编制程序体系之四Task的蛰伏 

  协作式裁撤中的关键项目是CancellationTokenSource。它有多少个重视脾气Token,Token是一个名字为CancellationToken的值类型。CancellationToken继而进一步提供了布尔值的性质IsCancellationRequested作为须要撤除职业的标志。CancellationToken还有一个措施特别值得注意,那正是Register方法。它承受传递二个Action委托,在线程结束的时候被回调,使用方式如:

  .NET 并行(多核)编制程序体系之5Task实践和丰硕管理 

cts.Token.Register(() =>
{
   Console.WriteLine("工作线程被终止了。");
});

  .NET 并行(多核)编制程序连串之6Task基础部分完成篇 

  而且Task对象对CancellationTokenSource对象是先个性支持的,在布局Task对象的时候就可以传进去CancellationTokenSource的实例。看叁个网络的小例子:

  .NET 并行(多核)编制程序类别之柒共享数据难题和减轻概述

static void Main(string[] args)
{
    CancellationTokenSource cts = new CancellationTokenSource();
    Task<int> t = new Task<int>(() => Add(cts.Token), cts.Token);
    t.Start();
    t.ContinueWith(TaskEnded);
    //等待按下任意一个键取消任务
    Console.ReadKey();
    cts.Cancel();
    Console.ReadKey();
}

static void TaskEnded(Task<int> task)
{
    Console.WriteLine("任务完成,完成时候的状态为:");
    Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted);
    Console.WriteLine("任务的返回值为:{0}", task.Result);
}

static int Add(CancellationToken ct)
{
    Console.WriteLine("任务开始……");
    int result = 0;
    while (!ct.IsCancellationRequested)
    {
        result++;
        Thread.Sleep(1000);
    }
    return result;
}

 

  不过要求小心,像下面这么写,使用ct.IsCancellationRequested推断一下,如若撤销频域信号被设定了,则脱离任务,那种情景CL智跑会以为是成功甘休的,那现实反应了技师的只求,IsCanceled会再次来到false。如若想出现IsCanceled为true的景况,那么程序就要改写成:

 

static void Main(string[] args)
{
    CancellationTokenSource cts = new CancellationTokenSource();
    Task<int> t = new Task<int>(() => AddCancleByThrow(cts.Token), cts.Token);
    t.Start();
    t.ContinueWith(TaskEndedByCatch);
    //等待按下任意一个键取消任务
    Console.ReadKey();
    cts.Cancel();
    Console.ReadKey();
}

static void TaskEndedByCatch(Task<int> task)
{
    Console.WriteLine("任务完成,完成时候的状态为:");
    Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted);
    try
    {
        Console.WriteLine("任务的返回值为:{0}", task.Result);
    }
    catch (AggregateException e)
    {
        e.Handle((err) => err is OperationCanceledException);
    }
}

static int AddCancleByThrow(CancellationToken ct)
{
    Console.WriteLine("任务开始……");
    int result = 0;
    while (true)
    {
        ct.ThrowIfCancellationRequested();
        result++;
        Thread.Sleep(1000);
    }
    return result;
}

本篇的争辨不多,代码的例子繁多。

  在职责完毕求值的办法TaskEndedByCatch中,要是职责是通过ThrowIfCancellationRequested方法结束的,对职分求结果值将会抛出万分OperationCanceledException,而不是收获抛出非常前的结果值。那象征职分是因此足够的章程被撤消掉的,所以能够小心到上边代码的输出中,状态IsCancled为true。同时您会发觉IsFaulted状态却依然等于false。那是因为ThrowIfCancellationRequested是合作式撤废情势类别CancellationTokenSource的3个格局,CL中华V进行了优良的拍卖。CLOdyssey知道这一行程序开辟者有意为之的代码,所以不把它当作是3个1二分(它被清楚为撤废)。要博得IsFaulted等于true的气象,本身手动在贰个地方抛出三个不胜试试就能够了。

 

  别的,CancellationTokenSource便是能够被八个Task共享的,那样能够撤废1组职分。撤除一组任务最简便易行的就是应用职责工厂。任务工厂补助四个职责之间共享相同的地方,如裁撤项目。通过运用职责工厂,能够同时收回1组职责:

在TPL中一个口径的操作正是”撤废Task”。之所以说它是个原则的操作,其实是把那几个操作和事先传统的十二线程编程进行相比来说的。

static void Main(string[] args)
{
   CancellationTokenSource cts = new CancellationTokenSource();
   //等待按下任意一个键取消任务
   TaskFactory taskFactory = new TaskFactory();
   Task[] tasks = new Task[]
   {
      taskFactory.StartNew(() => Add(cts.Token)),
      taskFactory.StartNew(() => Add(cts.Token)),
      taskFactory.StartNew(() => Add(cts.Token))
   };
   //CancellationToken.None指示TasksEnded不能被取消
   taskFactory.ContinueWhenAll(tasks, TasksEnded, CancellationToken.None);
   Console.ReadKey();
   cts.Cancel();
   Console.ReadKey();
}

static void TasksEnded(Task[] tasks)
{
   Console.WriteLine("所有任务已完成!");
}

 

  好了,看完线程的平常化撤除,再来看一下线程的这一个难点,这么些在上边也简单说了弹指间,那是1种技术员不愿意的线程结束的形式。

在从前的二十四线程编制程序中,我们一般是本人写一些代码来打消线程的周转。不过在.NET 四的TPL中就停放了撤回的措施,也许大家认为TPL无需内置那一个代码,因为太轻易了。不过那么些松开的不2秘籍不唯有只是收回了运维的Task,而且还减小了在撤消运营的Task时恐怕发生的某个高风险,大家三番四次小说会详细讲述。

线程的非凡管理

创办多个撤回的Task一般要开始展览上边一些步骤:

  先看上边包车型大巴事例:

 

Task.Factory.StartNew(() =>
{
    throw new Exception();
});

a.      
    a.创设System.Threading.CancellationTokenSource的一个实例:

   运营那段程序,你会开掘平昔未曾非凡抛出,线程中的十分会被线程忽略掉,那么些是我们没有供给的,我们必要知道卓殊发生了,并进行相应的管理。

 // create the cancellation token source
CancellationTokenSource tokenSource = new CancellationTokenSource();

  追踪那种主题材料,平常记日志是一种常用方法。其余通过本事手腕去捕获那个极度时另一种艺术,那是此处研讨的主要性。

b.   
    b.通过CancellationTokenSource.Token属性得到三个System.Threading.CancellationToken:

  Task线程中未捕获的越发会在废品回收时终结器实行线程中被抛出。大家得以因而GC.Collect来强制垃圾回收从而引发终结器管理线程,此时Task的未捕获至极会被抛出。比方:

    

//在Task中抛出异常
Task.Factory.StartNew(() =>
{
    throw new Exception();
});
//确保任务完成
Thread.Sleep(100);
//强制垃圾回收
GC.Collect();
//等待终结器处理
GC.WaitForPendingFinalizers();

CancellationToken token = tokenSource.Token;

  好了,非常抛出,程序崩溃了。不过那么些作为在.NET
四.5中又不无改观,直接运行那一个程序并不会抛出万分,而在App.config中加多如下配置以往,至极才会抛出:

     c.成立三个新的Task可能Task<T>,并且在构造函数传入Action或许Action<object>的寄托作为第二个参数,传入CancellationToken作为第3个参数:

<configuration>
    <runtime>
        <ThrowUnobservedTaskExceptions enabled="true"/>
    </runtime>
</configuration>

     

  抛出特别,程序崩溃并不是程序猿想要的作为,大家盼望的是足以捕获极度并拍卖之。要达到这几个指标,针对Task对象,大家得以行使的招数有那般多少个:调用Task.Wait/WaitAll,也许引用Task<T>.Result属性(那一个在地方的例证中已经应用了),或许最简易的引用Task.Exception属性来捕获Task的不胜。

Task task = new Task(new Action(printMessage), token);

  举个例子通过Task.Wait手动捕获AggregateException:

d.   
    d.调用Task的Start()方法。

try
{
    Task.WaitAll(
    Task.Factory.StartNew(() =>
    {
        throw new Exception();
    }));
}
catch (AggregateException)
{
    // 处理异常
    //...... 
}

 

  那样大家就抓获到了要命并得以管理它了。

    上面的步子和大家前面介绍的创始多少个Task的代码大致同一,只是在构造函数中多传入了三个参数。

  当然最轻易易行的正是一贯引用一下Task.Exception属性:

 

Task.Factory.StartNew(() =>
{
    throw new Exception();
}).ContinueWith(t => { 
    var exp = t.Exception;
    // 处理异常
    //...... 
});

    如若想要撤消多少个Task的周转,只要调用CancellationToken实例的Cancel()方法就能够了。

  同样的,大家捕获了万分,并且处理掉那些就可以了,像上面例子中的管理方式正是忽视界程卓殊,没做任何处理。

    有点要越发注意的,当大家调用了Cancel()方法之后,.NET
Framework不会强制性的去关闭运营的Task。

  其余,能够透过TaskContinuationOptions.OnlyOnFaulted来驱动唯有在发出尤其时才去奉行ContinueWith中钦定的行为,代码如下:

    大家友好必须去检查实验在此之前在开创Task时候传出的尤其CancellationToken。

Task.Factory.StartNew(() =>
{
    throw new Exception();
}).ContinueWith(t => { var exp = t.Exception; }, TaskContinuationOptions.OnlyOnFaulted);

    大家在创建Task是传播CancellationToken到构造函数,其实这几个CancellationToken便是.NET
Framework用来幸免我们再度运维已经被打消的Task,能够说就是二个阐明位。

  最终索要表达的是TaskScheduler.UnobservedTaskException事件,该事件是富有未捕获被抛出前的最后能够将其擒获的措施。通过UnobservedTaskException伊芙ntArgs.SetObserved方法来将丰裕标识为已破获。

 

TaskScheduler.UnobservedTaskException += (s, e) =>
{
    //设置所有未捕获异常被捕获
    e.SetObserved();
};

Task.Factory.StartNew(() =>
{
    throw new Exception();
});

    首先,进入第二个议题:

   好了,Task的有关难点就计算到那边了,上面将计算一下并行总计方面的学识,它们与Task对象之间实际存在着复杂的沟通。

  1.      
通过轮询的不2秘诀检验Task是还是不是被吊销

 

  在不少Task内部都饱含了循环,用来拍卖数量。大家能够在循环中经过CancellationToken的IsCancellationRequest属性来检查评定task是或不是被注销了。若是那么些天性为true,那么大家就得跳出循环,并且释放task所攻陷的能源(如数据库能源,文件财富等).

  大家也足以在task运行体中抛出System.Threading.OperationCanceledException来打消运转的task。

    代码如下:

4858.com 34858.com 4代码

 while (true)
{
    if (token.IsCancellationRequested)
    {
          // tidy up and release resources
           throw new OperationCanceledException(token);
    }
     else
     {
        // do a unit of work
       }
 }

 

  借使大家从没其他的财富要自由,那么只要轻巧的调用CancellationToken.ThrowIfCancellationRequested()方法,那一个方法会检查是否要收回task,并且抛出分外。代码如下:

 while (true)
{
        token.ThrowIfCancellationRequested();
         // do a unit of work
 }

 

 

  上面就付出有一个壹体化的事例:创制2个足以撤除的task,并且通过轮询不断的自己商讨是还是不是要收回task

  代码如下:

  

4858.com 54858.com 6代码

        static void Main(string[] args)
        {
            // create the cancellation token source
            CancellationTokenSource tokenSource = new CancellationTokenSource();

            // create the cancellation token
            CancellationToken token = tokenSource.Token;
            // create the task

            Task task = new Task(() =>
            {
                for (int i = 0; i < int.MaxValue; i++)
                {
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine(“Task cancel detected”);
                        throw new OperationCanceledException(token);
                    }
                    else
                    {
                        Console.WriteLine(“Int value {0}”, i);
                    }
                }
            }, token);

            // wait for input before we start the task
            Console.WriteLine(“Press enter to start task”);
            Console.WriteLine(“Press enter again to cancel task”);
            Console.ReadLine();

            // start the task
            task.Start();

            // read a line from the console.
            Console.ReadLine();

            // cancel the task
            Console.WriteLine(“Cancelling task”);
            tokenSource.Cancel();

            // wait for input before exiting
            Console.WriteLine(“Main method complete. Press enter to finish.”);
            Console.ReadLine();
        }

 

 

  贰.      
用委托delegate来检查评定Task是不是被撤回

 

  大家得以在注册两个信托到CancellationToken中,这么些委托的点子在CancellationToken.Cancel()调用从前被调用。

  大家能够用那么些委托中的方法来作为贰个检验task是不是被撤废的其余三个可选的方法,因为这几个法子是在Cancel()方法被调用在此之前就调用的,所以那几个委托中的方法能够检查评定task是或不是被cancel了,约等于说,只要那一个委托的主意被调用,那么就说那一个CancellationToken.Cancel()方法被调用了,而且在那么些委托的章程中大家能够做过多的事情,如通告用户裁撤操作发生了。

  上边包车型大巴代码给出了1个例子。

   

4858.com 74858.com 8代码

        static void Main(string[] args)
        {
            // create the cancellation token source
            CancellationTokenSource tokenSource = new CancellationTokenSource();

            // create the cancellation token
            CancellationToken token = tokenSource.Token;

            // create the task
            Task task = new Task(() =>
            {
                for (int i = 0; i < int.MaxValue; i++)
                {
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine(“Task cancel detected”);
                        throw new OperationCanceledException(token);
                    }
                    else
                    {
                        Console.WriteLine(“Int value {0}”, i);
                    }
                }
            }, token);

            // register a cancellation delegate
            token.Register(() =>
            {
                Console.WriteLine(“>>>>>> Delegate Invoked\n”);
            });

            // wait for input before we start the task
            Console.WriteLine(“Press enter to start task”);
            Console.WriteLine(“Press enter again to cancel task”);
            Console.ReadLine();

            // start the task
            task.Start();
            // read a line from the console.
            Console.ReadLine();

            // cancel the task
            Console.WriteLine(“Cancelling task”);
            tokenSource.Cancel();

            // wait for input before exiting
            Console.WriteLine(“Main method complete. Press enter to finish.”);
            Console.ReadLine();
        }

 

  叁.      
用Wait
Handle还检验Task是不是被撤除

  第两种情势检查评定task是不是被cancel便是调用CancellationToken.WaitHandle属性。对于那几个个性的详细使用,在承接的篇章中会深切的叙说,在此处关键知道一点就行了:CancellationToken的WaitOne()方法会阻止task的周转,唯有CancellationToken的cancel()方法被调用后,这种阻碍才会自由。

 

  在底下的事例中,成立了五个task,当中task二调用了WaitOne()方法,所以task贰一贯不会运作,除非调用了CancellationToken的Cancel()方法,所以WaitOne()方法也终于检查评定task是或不是被cancel的壹种办法了。

 

4858.com 94858.com 10代码

        static void Main(string[] args)
        {

            // create the cancellation token source
            CancellationTokenSource tokenSource = new CancellationTokenSource();

            // create the cancellation token
            CancellationToken token = tokenSource.Token;

            // create the task
            Task task1 = new Task(() =>
            {
                for (int i = 0; i < int.MaxValue; i++)
                {
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine(“Task cancel detected”);
                        throw new OperationCanceledException(token);
                    }
                    else
                    {
                        Console.WriteLine(“Int value {0}”, i);
                    }
                }
            }, token);

            // create a second task that will use the wait handle
            Task task2 = new Task(() =>
            {
                // wait on the handle
                token.WaitHandle.WaitOne();
                // write out a message
                Console.WriteLine(“>>>>> Wait handle released”);
            });

            // wait for input before we start the task
            Console.WriteLine(“Press enter to start task”);
            Console.WriteLine(“Press enter again to cancel task”);
            Console.ReadLine();
            // start the tasks
            task1.Start();
            task2.Start();

            // read a line from the console.
            Console.ReadLine();

            // cancel the task
            Console.WriteLine(“Cancelling task”);
            tokenSource.Cancel();

            // wait for input before exiting
            Console.WriteLine(“Main method complete. Press enter to finish.”);
            Console.ReadLine();
        }

 

  4.     
打消八个Task

  大家能够使用贰个CancellationToken来创立多少个例外的Tasks,当以此CancellationToken的Cancel()方法调用的时候,使用了那几个token的两个task都会被吊销。

 

4858.com 114858.com 12代码

        static void Main(string[] args)
        {
            // create the cancellation token source
            CancellationTokenSource tokenSource = new CancellationTokenSource();

            // create the cancellation token
            CancellationToken token = tokenSource.Token;

            // create the tasks
            Task task1 = new Task(() =>
            {
                for (int i = 0; i < int.MaxValue; i++)
                {
                    token.ThrowIfCancellationRequested();
                    Console.WriteLine(“Task 1 – Int value {0}”, i);
                }
            }, token);

            Task task2 = new Task(() =>
            {
                for (int i = 0; i < int.MaxValue; i++)
                {
                    token.ThrowIfCancellationRequested();
                    Console.WriteLine(“Task 2 – Int value {0}”, i);
                }
            }, token);
            // wait for input before we start the tasks
            Console.WriteLine(“Press enter to start tasks”);
            Console.WriteLine(“Press enter again to cancel tasks”);
            Console.ReadLine();

            // start the tasks
            task1.Start();
            task2.Start();

            // read a line from the console.
            Console.ReadLine();

            // cancel the task
            Console.WriteLine(“Cancelling tasks”);
            tokenSource.Cancel();
            // wait for input before exiting
            Console.WriteLine(“Main method complete. Press enter to finish.”);
            Console.ReadLine();
        }

 

  5.
创立组合的撤销Task的Token

  我们得以用CancellationTokenSource.CreateLinkedTokenSource()方法来创建一个整合的token,这些组成的token有众多的CancellationToken组成。首要构成token中的任性三个token调用了Cancel()方法,那么使用那么些组成token的有着task就能够被打消。代码如下:

 

4858.com 134858.com 14代码

        static void Main(string[] args)
        {
            // create the cancellation token sources
            CancellationTokenSource tokenSource1 = new CancellationTokenSource();
            CancellationTokenSource tokenSource2 = new CancellationTokenSource();
            CancellationTokenSource tokenSource3 = new CancellationTokenSource();

            // create a composite token source using multiple tokens
            CancellationTokenSource compositeSource = 
                CancellationTokenSource.CreateLinkedTokenSource(
            tokenSource1.Token, tokenSource2.Token, tokenSource3.Token);

            // create a cancellable task using the composite token
            Task task = new Task(() =>
            {
                // wait until the token has been cancelled
                compositeSource.Token.WaitHandle.WaitOne();
                // throw a cancellation exception
                throw new OperationCanceledException(compositeSource.Token);
            }, compositeSource.Token);

            // start the task
            task.Start();

            // cancel one of the original tokens
            tokenSource2.Cancel();

            // wait for input before exiting
            Console.WriteLine(“Main method complete. Press enter to finish.”);
            Console.ReadLine();
        }

 

  陆.     
剖断二个Task是或不是业已被注销了

  能够运用Task的IsCancelled属性来决断task是不是被收回了。代码如下:

 

4858.com 154858.com 16代码

        static void Main(string[] args)
        {
            // create the cancellation token source
            CancellationTokenSource tokenSource1 = new CancellationTokenSource();

            // create the cancellation token
            CancellationToken token1 = tokenSource1.Token;

            // create the first task, which we will let run fully
            Task task1 = new Task(() =>
            {
                for (int i = 0; i < 10; i++)
                {
                    token1.ThrowIfCancellationRequested();
                    Console.WriteLine(“Task 1 – Int value {0}”, i);
4858.com,                }
            }, token1);

            // create the second cancellation token source
            CancellationTokenSource tokenSource2 = new CancellationTokenSource();

            // create the cancellation token
            CancellationToken token2 = tokenSource2.Token;

            // create the second task, which we will cancel
            Task task2 = new Task(() =>
            {
                for (int i = 0; i < int.MaxValue; i++)
                {
                    token2.ThrowIfCancellationRequested();
                    Console.WriteLine(“Task 2 – Int value {0}”, i);
                }
            }, token2);

            // start all of the tasks
            task1.Start();
            task2.Start();

            // cancel the second token source
            tokenSource2.Cancel();
            // write out the cancellation detail of each task
            Console.WriteLine(“Task 1 cancelled? {0}”, task1.IsCanceled);
            Console.WriteLine(“Task 2 cancelled? {0}”, task2.IsCanceled);
            // wait for input before exiting
            Console.WriteLine(“Main method complete. Press enter to finish.”);
            Console.ReadLine();
        }

 

后天就写到这里,相比的简易,都以局地很基础的事物,唯有这么,前边深刻疏解的时候才更为的顺风。

多谢各位!

 

   版权为小洋和今日头条全数,转载请标明出处给小编。

   

 

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有