我是伊只雄熊

导航

异步多线程 Task理解

一、简介

  Task是.NET Framework4.0 TPL(任务并行库)提供的新的操作线程池线程的封装类。它提供等待、终止(取消)、返回值、完成通知、失败通知、控制执行的先后次序等优化线程操作功能。Task(任务)并不是线程,任务运行的时候需要使用线程,但并不是说任务取代了线程,任务代码是使用底层的线程(Thread或ThreadPool线程)运行的,任务与线程之间并没有一对一的关系。

二、Task创建与启动

  Task类创建的任务使用的是后台线程,所以在前台线程全部终止的时候,如果任务还没有全部执行完,就会被被动终止。创建一个新的任务时,任务调度器(默认是基于线程池的TaskScheduler调度器,ThreadPoolTaskScheduler)会找到一个最合适的工作者线程,然后将任务加入该工作者线程的本地队列(每个工作者线程都有对应本地队列),任务所包含的代码会在该线程中运行。

  

  先定义一个任务要执行的方法:

  

static void NewTask()
        {
            Console.WriteLine("开始一个任务");
            Console.WriteLine("Task id:{0}",Task.CurrentId);
            Console.WriteLine("任务执行完成");
        }

  再创建和启动一个任务,有以下三种方式:

  1、使用TaskFactory创建一个任务

     TaskFactory tf = new TaskFactory();

    //任务就会立即启

    Task t1 = tf.StartNew(NewTask);

  2、使用Task类的Factory创建一个任务

    Task t2 = Task.Factory.StartNew(NewTask);

  3、Task构造函数并Start

    Task t3 = new Task(NewTask);

    t3.Start();

    Task t4 = new Task(NewTask, TaskCreationOptions.PreferFairness);

    t4.Start();

    //因为任务是后台线程,所以我们这里阻塞主线程一秒钟来等待任务全部执行完成

    Thread.Sleep(1000);

  注意:使用Task类的构造函数(第3种)和TaskFactory类的StartNew()方法(第2种)时,都可以传递TaskCreationOptions枚举中的值。TaskCreationOptions如下:

  

  注意:如果当前Task上的TaskCreationOptions设置为LongRunning的话,这个task就会委托到Thread中去执行,长时间运行的task占用着ThreadPool的线程,这时候ThreadPool为了保证线程充足,会再次开辟一些Thread,如果耗时任务此时释放了,会导致ThreadPool线程过多,上下文切换频繁,所以此时让Task在Thread中执行还是非常不错的选择,当然不指定这个LongRunning的话,就是在ThreadPool上执行。

三、Task状态与生命周期 

  任务Task有以下代表任务完成时状态的属性:

    1、IsCanceled,因为被取消而完成

    2、IsCompleted,成功完成

    3、IsFaulted,因为发生异常而完成

  任务并没有提供回调事件来通知完成(像BackgroundWorker一样),通过启用一个新任务的方式来完成类似的功能。 ContinueWith方法可以在一个任务完成的时候发起一个新任务,天然支持了任务的完成通知,可以在新任务中获取原任务的结果值。

 

四、Parallel

  使用Parallel.For、Parallel.ForEach的循环迭代的并行执行,TPL会在后台创建System.Threading.Tasks.Task的实例。使用Parallel.Invoke时,TPL也会创建与调用的委托数目一致的System.Threading.Tasks.Task的实例。

五、Task异常处理 

  当很多任务并行运行的时候,可能会并行发生很多异常。Task实例能够处理一组一组的异常,这些异常有System.AggregateException类处理。

class Program
    {
        private static ConcurrentQueue<Product> queue = null;
        static void Main(string[] args)
        {
            queue = new ConcurrentQueue<Product>();
            System.Threading.CancellationTokenSource token = new CancellationTokenSource();
            Task tk1 = Task.Factory.StartNew(() => SetProduct(token.Token));
            Thread.Sleep(2000);
       //检查状态,是否因为异常而导致失败
if (tk1.IsFaulted) { //循环输出异常 foreach (Exception ex in tk1.Exception.InnerExceptions) { Console.WriteLine("tk1 Exception:{0}", ex.Message); } } Console.ReadLine(); } static void SetProduct(System.Threading.CancellationToken ct) { for (int i = 0; i < 5; i++) { throw new Exception(string.Format("Exception Index {0}", i)); } Console.WriteLine("SetProduct 执行完成"); } }

 

六、Task取消

  通过取消标记来中断Task实例的执行。 CancellationTokenSource,CancellationToken下的IsCanceled属性标志当前是否已经被取消,取消任务,任务也不一定会马上取消。

class Program
    {
        private static ConcurrentQueue<Product> queue = null;
        /*  coder:释迦苦僧    */
        static void Main(string[] args)
        {
            queue = new ConcurrentQueue<Product>();
            System.Threading.CancellationTokenSource token = new CancellationTokenSource();
            Task tk1 = Task.Factory.StartNew(() => SetProduct(token.Token));
            Task tk2 = Task.Factory.StartNew(() => SetProduct(token.Token));
            Thread.Sleep(10);
            //取消任务操作
            token.Cancel();
            try
            {
                //等待完成
                Task.WaitAll(new Task[] { tk1, tk2 });
            }
            catch (AggregateException ex)
            {
                //如果当前的任务正在被取消,那么还会抛出一个TaskCanceledException异常,这个异常包含在AggregateException异常中
                Console.WriteLine("tk1 Canceled:{0}", tk1.IsCanceled);
                Console.WriteLine("tk1 Canceled:{0}", tk2.IsCanceled);
            }

            Thread.Sleep(2000);
            Console.WriteLine("tk1 Canceled:{0}", tk1.IsCanceled);
            Console.WriteLine("tk1 Canceled:{0}", tk2.IsCanceled);
            Console.ReadLine();
        }
        static void SetProduct(System.Threading.CancellationToken ct)
        {
             //每一次循环迭代,都会有新的代码调用 ThrowIfCancellationRequested 
             //这行代码能够对 OpreationCanceledException 异常进行观察
             //并且这个异常的标记与Task实例关联的那个标记进行比较,如果两者相同 ,而且IsCancelled属性为True,那么Task实例就知道存在一个要求取消的请求,并且会将状态转变为Canceled状态,中断任务执行。  
             //如果当前的任务正在被取消,那么还会抛出一个TaskCanceledException异常,这个异常包含在AggregateException异常中
            //检查取消标记
            ct.ThrowIfCancellationRequested();
            for (int i = 0; i < 50000; i++)
            {
                Product model = new Product();
                model.Name = "Name" + i;
                model.SellPrice = i;
                model.Category = "Category" + i;
                queue.Enqueue(model);
            
                ct.ThrowIfCancellationRequested();
            }
            Console.WriteLine("SetProduct   执行完成");
        }
    }
    class Product
    {
        public string Name { get; set; }
        public string Category { get; set; }
        public int SellPrice { get; set; }
    }

七、Task.WaitAll 并限定时长

  10毫秒没有完成任务,则输出了****

  queue = new ConcurrentQueue<Product>();
            Task tk1 = new Task(() => { SetProduct(1); SetProduct(3);});
            Task tk2 = new Task(() => SetProduct(2));
            tk1.Start();
            tk2.Start();

            //如果tk1 tk2 没能在10毫秒内完成 则输出 *****  
            if (!Task.WaitAll(new Task[] { tk1, tk2 }, 10))
            {
                Console.WriteLine("******");
            }
          
            Console.ReadLine();

八、Task返回值  Task<TResult>

 class Program
    {
        
        static void Main(string[] args)
        {
            Task<List<Product>> tk1 = Task<List<Product>>.Factory.StartNew(() => SetProduct());
            Task.WaitAll(tk1);
            Console.WriteLine(tk1.Result.Count);
            Console.WriteLine(tk1.Result[0].Name);
            Console.ReadLine();
        }
        static List<Product> SetProduct()
        {
            List<Product> result = new List<Product>();
            for (int i = 0; i < 500; i++)
            {
                Product model = new Product();
                model.Name = "Name" + i;
                model.SellPrice = i;
                model.Category = "Category" + i;
                result.Add(model);
            }
            Console.WriteLine("SetProduct   执行完成");
            return result;
        }
    }

九、连续执行多个任务 ContinueWith

class Program
    {
       
        static void Main(string[] args)
        {
            //创建任务t1
            Task t1 = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("执行 t1 任务");
                SpinWait.SpinUntil(() =>
                {
                    return false;
                }, 2000);

            });
            //创建任务t2   t2任务的执行 依赖与t1任务的执行完成
            Task t2 = t1.ContinueWith((t) =>
            {
                Console.WriteLine("执行 t2 任务"); 
                SpinWait.SpinUntil(() =>
                {
                    return false;
                }, 2000);

            });    
            //创建任务t3   t3任务的执行 依赖与t2任务的执行完成
            Task t3 = t2.ContinueWith((t) =>
            {
                Console.WriteLine("执行 t3 任务");
            });
            Console.ReadLine();
        }
    }

 

posted on 2018-02-07 17:40  我是伊只雄熊  阅读(1014)  评论(0编辑  收藏  举报