简介.NET 4.0的多工执行利器--Task

先从最简单的开始。test1()用以另一条Thread执行Thread.Sleep()及Console.WriteLine(),效果与ThreadPool.QueueUserWorkItem()相当。

排版显示纯文字

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Threading; using System.Diagnostics;

namespace TaskLab { class Program { static void Main( string [] args) { test1(); Console.Read(); }

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

} StartNew()完会立刻执行下一行,故会先看到Aync Run,1秒后印出Done。

Async Run...Done! 同时启动数个作业多工并行,但要等待各作业完成再继续下一步是常见的应用情境,传统上可透过WaitHandle、AutoResetEvent、ManualResetEvent等机制实现;Task的写法相对简单,建立多个Task物件,再当成Task.WaitAny()或Task.WaitAll()的参数就搞定啰!

排版显示纯文字

    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, task2);
        Console.WriteLine( "WaitAny Passed" );
        //等待两项作业都完成才会继续执行
        Task.WaitAll(task1, task2);
        Console.WriteLine( "WaitAll Passed" );
    }

task1耗时3秒、task2耗时5秒,所以3秒后WaitAny()执行完成、5秒后WaitAll()执行完毕。

Done!(3s)WaitAny PassedDone!(5s)WaitAll Passed 如果要等待多工作业传回结果,透过StartNew<T>()指定传回型别建立作业,随后以Task.Result取值,不用额外写Code就能确保多工作业执行完成后才读取结果继续运算。

排版显示纯文字

    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执行并传回结果的时间。

Done!Duration: 2,046ms 如果要安排多工作业完成后接连执行另一段程式,可使用ContinueWith():

排版显示纯文字

    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完成后才被执行。

Async Run...Done!In ContinueWith .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()耗时2秒,最后一个ContinueWith()接续在4秒后执行。

59:13-Async Run...59:15-Done59:15-ContinueWith 159:17-ContinueWith 2 最后一个例子比较复杂。ContinueWith()中的Action<Task>都会有一个输入参数,借以得知前一Task的执行状态,有IsCompleted, IsCanceled, IsFaulted几个属性可用。

要取消执行,得借助CancellationTokenSource及其所属CancellationToken类别,做法是在Task中持续呼叫CancellationToken.ThrowIfCancellationRequested(),一旦外部呼叫CancellationTokenSource.Cancel(),便会触发OperationCanceledException,Task有机制侦测此种例外状况,将结束作业执行后续的ContinueWith(),并指定Task.IsCanceled为True以为识别;而当Task程式发生Exception,也会结束作业触发ContinueWith(),此时则Task.IsFaulted为True,ContinueWith()中可透过Task.Exception.InnerExceptions取得错误细节。

以下程式同时可测试Task正常、取消及错误三种情境,使用者透过输入1,2或3来决定要测试哪一种。在Task外先宣告一个CancellationTokenSource类别,将其中的Token属性当成StartNew()的第二项参数,而Task中则保留最初的五秒可以取消,方法是每隔一秒呼叫一次CancellationToken.ThrowIfCancellationRequested(),当程式外部呼叫CancellationTokenSource.Cancel(),Task就会结束。5秒后若未取消,再依使用者决定的测试情境return结果或是抛出Exception。ContinueWith()则会检查IsCanceled, IsFaulted等旗标,并输出结果。

排版显示纯文字

static void test6() { CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken cancelToken = cts.Token; 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: //选2时 throw new ApplicationException( "MyException" ); } 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.Flattern().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(); } } 以下是三种测试情境的结果。

正常执行:

Test Option 1, 2 or 3 (1-Complete / 2-Cancel / 3-Fault) : 1Async Run...IsCompleted: True IsCanceled: False IsFaulted: FalseCompleted! Result=OK 取消: (IsCanceled为True,但留意IsCompleted也算True)

Test Option 1, 2 or 3 (1-Complete / 2-Cancel / 3-Fault) : 2Async Run...IsCompleted: True IsCanceled: True IsFaulted: FalseCanceled! 错误: (IsFaulted为True,IsCompleted也是True)

Test Option 1, 2 or 3 (1-Complete / 2-Cancel / 3-Fault) : 3Async Run...IsCompleted: True IsCanceled: False IsFaulted: TrueFaulted!Error: MyException 【小结】

说穿了,Task能做的事,过去使用Thread/ThreadPool配合Event、WaitHandle一样能办到,但使用Task能以较简洁的语法完成相同工作,使用.NET 4.0开发多工作业程式应可多加利用。同时,Task也是.NET 4.5 async await的基础概念之一,值得大家花点时间熟悉,有益无害。

本文摘抄于网络,出处http://blog.darkthread.net/post-2012-07-20-net4-task.aspx

转载于:https://my.oschina.net/908Sharp/blog/202609

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值