C# 异步编程3 TPL Task 异步程序开发

.Net在Framework4.0中增加了任务并行库,对开发人员来说利用多核多线程CPU环境变得更加简单,TPL正符合我们本系列的技术需求。因TPL涉及内容较多,且本系列文章为异步程序开发,所以本文并未涉及TPL全部内容。后续会写一个TPL系列的Blog,各位朋友可以关注一下。

TASK

TPL的基础Task,Task是TPL并行编程的最小单元,即表示一个异步操作。利用Task进行异步编程非常简单:

        static void Main(string[] args)
        {
            BaseTaskDemo();
            //BaseTaskDemo2();//两者效果相同
            Console.WriteLine(String.Format("Main 线程:{0}", Thread.CurrentThread.ManagedThreadId));
            Console.ReadLine();
        }
        private static void BaseTaskDemo()
        {
            var task = new Task(() => {
                Thread.Sleep(2000);
                Console.WriteLine(String.Format("Task 线程:{0}", Thread.CurrentThread.ManagedThreadId));
            });
            task.Start();
        }
        private static void BaseTaskDemo2()
        {
            var task = Task.Run(() =>
            {
                Thread.Sleep(2000);
                Console.WriteLine(String.Format("Task 线程:{0}", Thread.CurrentThread.ManagedThreadId));
            });
        }

 

程序说明:

1、new Task(Action)创建一个异步任务,参数Action是异步任务要执行的委托对象

2、task.Start()启动异步任务的执行

3、Task.Run(Action)等效于1、2的组合

上面的示例程序实现了异步操作,但主线程无法获知异步任务完成与否。为获取Task的执行结果,上面的程序进行如下修改:

        static void Main(string[] args)
        {
            var task = TaskForResult();
            Console.WriteLine(String.Format("Main 线程:{0}", Thread.CurrentThread.ManagedThreadId));
            task.Wait();
            if (task.IsCompleted)
                Console.WriteLine(String.Format("获取异步执行结果:{0}", task.Result));
            Console.ReadLine();
        }
        private static Task<int> TaskForResult()
        {
            var task = Task.Run(()=> {
                Thread.Sleep(2000);
                Console.WriteLine(string.Format("Task 线程:{0},Task执行完成。", Thread.CurrentThread.ManagedThreadId));
                return 10;
            });
            return task;
        }

程序说明:

1、下面代码的原型为Task.Run(Func<int>),Run的参数不再是Action,因为在该任务中我们要返回一个int值,所以应该使用Run(Func<T>)这个重载。

var task = Task.Run(()=> {
    Thread.Sleep(2000);
    Console.WriteLine(string.Format("Task 线程:{0},Task执行完成。", Thread.CurrentThread.ManagedThreadId));
    return 10;
});

2、主线程中调用task.Wait()时,主线程将一直等待异步任务完成或被取消。

3、task.IsCompleted属性用于判断异步任务是否完成

4、task.Result获取异步任务的执行结果(返回值)

上面的示例程序已经实现了主线程获取异步程序的状态及返回值,但如果异步程序非常耗时,则存在主线程需要临时取消耗时异步程序执行的功能。为了满足上述要求,程序可做如下调整:

        static void Main(string[] args)
        {
            CancellationTokenSource tokenSource = new CancellationTokenSource(5000);
            var task = TaskForResult2(tokenSource);
            Console.WriteLine(String.Format("Main 线程:{0}", Thread.CurrentThread.ManagedThreadId));
            Console.WriteLine(String.Format("Main 线程:{0},获取异步执行结果:{1}", Thread.CurrentThread.ManagedThreadId, task.Result));
            Console.ReadLine();
        }
        private static Task<int> TaskForResult2(CancellationTokenSource tokenSource)
        {
            var task = Task.Run(() =>
            {
                Thread.Sleep(10000);
                if (!tokenSource.IsCancellationRequested)
                {
                    Console.WriteLine(String.Format("Task 线程:{0},任务1执行完成。", Thread.CurrentThread.ManagedThreadId));
                    return 10;
                }
                else
                {
                    return -1;
                }
            }, tokenSource.Token);
            return task;
        }

程序说明:

1、CancellationTokenSource提供任务取消消息,构造参数 5000 表示CancellationTokenSource在发出5s后超时并取消

2、在Task的委托内部 tokenSource.IsCancellationRequested 获取取消标记

3、task.Result会隐式调用Wait()方法

如果异步Task在执行过程中出现异常,则需要对发生的异常做出响应:

        static void Main(string[] args)
        {
            CancellationTokenSource tokenSource = new CancellationTokenSource(5000);
            var task = TaskForResult2(tokenSource);
            Console.WriteLine(String.Format("Main 线程:{0}", Thread.CurrentThread.ManagedThreadId));
            try
            {
                //task.Wait();
                Console.WriteLine(String.Format("Main 线程:{0},获取异步执行结果:{1}", Thread.CurrentThread.ManagedThreadId, task.Result));
            }
            catch (AggregateException ex)
            {

            }
            Console.ReadLine();
        }
        private static Task<int> TaskForResult2(CancellationTokenSource tokenSource)
        {
            var task = Task.Run(() =>
            {
                Thread.Sleep(1000);
                if (!tokenSource.IsCancellationRequested)
                {
                    throw new Exception("抛出异常");
                }
                else
                {
                    return -1;
                }
            }, tokenSource.Token);
            return task;
        }

程序说明:

1、在Task中引发的异常需要在 task.Wait()或task.Result时捕获

 

写在后面:Task的功能远不止上述这些,如Task多任务串行、TaskFactory、Paralle等知识非常有趣和重要。如果你感兴趣的话,可以关注本人后续TPL的文章。

 
posted @ 2017-09-12 14:39  DW039  阅读(1147)  评论(2编辑  收藏  举报