• Task


     在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击,因为task会比thread具有更小的性能开销,不过大家肯定会有疑惑,任务和线程到底有什么区别?

    1:任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行。
    2:任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制。

    1.Task

        class Program
        {
            static void Main(string[] args)
            {
                //第一种方式开启
                var task1 = new Task(() =>
                {
                    Run1();
                });
    
                //第二种方式开启
                var task2 = Task.Factory.StartNew(() =>
                {
                    Run2();
                });
    
                Console.WriteLine("调用start之前****************************
    ");
    
                //调用start之前的“任务状态”
                Console.WriteLine("task1的状态:{0}", task1.Status);
                Console.WriteLine("task2的状态:{0}", task2.Status);
                task1.Start();
                Console.WriteLine("
    调用start之后****************************");
    
                //调用start之前的“任务状态”
                Console.WriteLine("
    task1的状态:{0}", task1.Status);
                Console.WriteLine("task2的状态:{0}", task2.Status);
    
                //主线程等待任务执行完
                Task.WaitAll(task1, task2);
                Console.WriteLine("
    任务执行完后的状态****************************");
    
                //调用start之前的“任务状态”
                Console.WriteLine("
    task1的状态:{0}", task1.Status);
                Console.WriteLine("task2的状态:{0}", task2.Status);
                Console.Read();
            }
    
            static void Run1()
            {
                Thread.Sleep(1000);
                Console.WriteLine("
    我是任务1");
            }
    
            static void Run2()
            {
                Thread.Sleep(2000);
                Console.WriteLine("我是任务2");
            }
        }

     

    从图中可以看出两种task实例的简略生命周期。
    Created:表示默认初始化任务,但是我们发现“工厂创建的”实例直接跳过。
    WaitingToRun: 这种状态表示等待任务调度器分配线程给任务执行。
    RanToCompletion:任务执行完毕。

    2. 取消任务

    我们知道task是并行计算的,比如说主线程在某个时刻由于某种原因要取消某个task的执行,我们能做到吗?
    当然我们可以做到,在4.0中给我们提供一个“取消标记”叫做CancellationTokenSource.Token,在创建task的时候传入此参数,就可以将主线程和任务相
    关联,然后在任务中设置“取消信号“叫做ThrowIfCancellationRequested来等待主线程使用Cancel来通知,一旦cancel被调用。
    task将会抛出OperationCanceledException来中断此任务的执行,
    最后将当前task的Status的IsCanceled属性设为true。

    class Program
    {
        static void Main(string[] args)
        {
            var cts = new CancellationTokenSource();
            var ct = cts.Token;
    
            Task task1 = new Task(() => { Run1(ct); }, ct);
    
            Task task2 = new Task(Run2);
    
            try
            {
                task1.Start();
                task2.Start();
    
                Thread.Sleep(1000);
    
                cts.Cancel();
    
                Task.WaitAll(task1, task2);
            }
            catch (AggregateException ex)
            {
                foreach (var e in ex.InnerExceptions)
                {
                    Console.WriteLine("
    hi,我是OperationCanceledException:{0}
    ", e.Message);
                }
    
                //task1是否取消
                Console.WriteLine("task1是不是被取消了? {0}", task1.IsCanceled);
                Console.WriteLine("task2是不是被取消了? {0}", task2.IsCanceled);
            }
    
            Console.Read();
        }
    
        static void Run1(CancellationToken ct)
        {
            ct.ThrowIfCancellationRequested();
    
            Console.WriteLine("我是任务1");
    
            Thread.Sleep(2000);
    
            ct.ThrowIfCancellationRequested();
    
            Console.WriteLine("我是任务1的第二部分信息");
        }
    
        static void Run2()
        {
            Console.WriteLine("我是任务2");
        }
    }

    从图中可以看出
    ①:Run1中的Console.WriteLine("我是任务1的第二部分信息"); 没有被执行。
    ②:Console.WriteLine("task1是不是被取消了? {0}", task1.IsCanceled); 状态为True。
    也就告诉我们Run1中途被主线程中断执行,我们coding的代码起到效果了。

    3. 获取任务的返回值

    我们以前写线程的时候注册的方法一般都是void类型,如果主线程要从工作线程中获取数据一般采用的手段是“委托+事件”的模式,然而
    在Task中有两种方式可以解决。
    <1> 现在我们的实例化是采用Task<TResult>的形式,其中TResult就是当前task执行后返回的结果,下面举得例子是t2任务获取t1的执行结果。

        class Program
        {
            static void Main(string[] args)
            {
                //执行task1
                var t1 = Task.Factory.StartNew<List<string>>(() => { return Run1(); });
                t1.Wait();
                var t2 = Task.Factory.StartNew(() =>
                {
                    Console.WriteLine("t1集合中返回的个数:" + string.Join(",", t1.Result));
                });
                Console.WriteLine("主程序结束");
                Console.Read();
            }
    
            static List<string> Run1()
            {
                Thread.Sleep(1000);
                return new List<string> { "1", "4", "8" };
            }
        }

    <2>采用ContinueWith方法,很有意思,现在我们将上面的方法改造一下。

        class Program
        {
            static void Main(string[] args)
            {
                //执行task1
                var t1 = Task.Factory.StartNew<List<string>>(() => { return Run1(); });
                t1.Wait();
                var t2 = t1.ContinueWith((i) =>
                {
                    Console.WriteLine("t1集合中返回的个数:" + string.Join(",", i.Result));
                });
    
                Console.WriteLine("主程序结束");
                Console.Read();
            }
    
            static List<string> Run1()
            {
                Thread.Sleep(1000);
                return new List<string> { "1", "4", "8" };
            }
        }

    4.ContinueWith结合WaitAll来玩一把

     当这两者结合起来,我们就可以玩一些复杂一点的东西,比如说现在有7个任务,其中t1需要串行,t2-t3可以并行,t4需要串行,t5-t6并行,t7串行。

        class Program
        {
            static void Main(string[] args)
            {
                ConcurrentStack<int> stack = new ConcurrentStack<int>();
                //t1先串行
                var t1 = Task.Factory.StartNew(() =>
                {
                    stack.Push(1);
                    stack.Push(2);
                });
    
                //t2,t3并行执行
                var t2 = t1.ContinueWith(t =>
                {
                    int result;
                    stack.TryPop(out result);
                    Console.WriteLine("t2 out {0}",result);
                });
    
                //t2,t3并行执行
                var t3 = t1.ContinueWith(t =>
                {
                    int result;
                    stack.TryPop(out result);
                    Console.WriteLine("t3 out {0}", result);
                });
    
                //等待t2和t3执行完
                Task.WaitAll(t2, t3);
    
                //t4串行执行
                var t4 = Task.Factory.StartNew(() =>
                {
                    stack.Push(1);
                    stack.Push(2);
                });
    
                //t5,t6并行执行
                var t5 = t4.ContinueWith(t =>
                {
                    int result;
                    stack.TryPop(out result);
                    Console.WriteLine("t5 out {0}", result);
                });
    
                //t5,t6并行执行
                var t6 = t4.ContinueWith(t =>
                {
                    int result;
                    //只弹出,不移除
                    stack.TryPeek(out result);
                    Console.WriteLine("t6 out {0}", result);
                });
    
                //临界区:等待t5,t6执行完
                Task.WaitAll(t5, t6);
    
                //t7串行执行
                var t7 = Task.Factory.StartNew(() =>
                {
                    Console.WriteLine("当前集合元素个数:" + stack.Count);
                });
    
                Console.Read();
            }
        }

  • 相关阅读:
    编写测试类实现并发访问固定URL(亲测能用!!!)
    java项目添加log4j打印日志+转换系统时间
    springboot项目没错,但就是报红叉
    我想查看数据库名,输入命令:select name from v$database;为什么会说表和视图不存在
    DRUID连接池的实用 配置详解+使用方法+监控方式(太强大了!!!)
    Druid连接池 属性说明
    springBoot2.2.0+mybatis-xml文件方式+Oracle11g+jsp页面,实现简单的CRUD
    s5-12 RIP
    s5-12 RIP
    s5-13 RIP 为什么会 衰败
  • 原文地址:https://www.cnblogs.com/lgxlsm/p/7560745.html
Copyright © 2020-2023  润新知