C#基础知识学习——异步Task、Parallel 、 异步实例(十九)

异步定义

异步用途 通常用于执行完成时间较长的任务,比如:打开大文件、连接远程计算机、查询数据库等操作。应用程序调用方法异步执行某个操作时,应用程序可以在异步方法执行其任务时继续执行。
异步机制 异步编程采用future或callback机制,以避免产生不必要的线程。
异步核心 启动了的操作将在一段时间后完成, 这个操作正在执行时,不会阻塞原来的线程。启动了这个操作的线程可以继续执行其他任务,当操作完成时,会通知回调函数。
异步与同步异步与同步是相对于同步而言,跟多线程不能同一而论。同步执行是执行某个任务时,需要在该任务完成后才能继续执行另一个任务。异步执行在执行某个任务时,可以在该任务完成之前执行另一个任务。异步执行最重要的体现就是不排队,不阻塞。
单线程执行
在这里插入图片描述
多线程执行
在这里插入图片描述

异步与多线程

异步可以在单个线程上实现,也可以在多个线程上实现,还可以不需要线程(一些IO操作)。
只要存在多线程就存在异步,多线程就是异步的表现。

异步应用

.NET Framework的许多方面都支持异步编程功能:

  • 文件IO、流IO、套接字IO
  • 网络
  • 远程处理信道:HTTP、TCP和代理
  • 使用ASP.NET创建的XML WebServices
  • ASP.NET Web窗体
  • 使用MessageQuenue类的消息队列

.Net Framework为异步操作提供了两种设计模式

  • 使用IAsyncReault对象的异步操作
  • 使用事件的异步操作

Task

Task本身就表示一个异步操作,默认是运行在线程池里的线程,更轻量,可以更高效的利用线程:

  • 实现了控制任务执行的顺序
  • 实现父子任务
  • 实现任务的取消操作
  • 实现了进度报告
  • 实现了返回值
  • 实现了随时查看任务状态

Task启动与同步

Task.RunSynchronously

          //启用线程池中的线程异步执行
            Task t1 = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Task--1启动...");
            });
            //启用线程池中的线程异步执行
            Task t2 = Task.Run(() =>
            {
                Console.WriteLine("Task--2启动...");
            });

            Task t3 = new Task(() =>
            {
                Console.WriteLine("Task--3启动...");
            });
           // t3.Start();//启用线程池中的线程异步执行
            t3.RunSynchronously();//任务与主线程同步执行

Task.WaitAll

            Task t1 = Task.Run(() =>
            {
                Console.WriteLine("Task--1启动...");
            });
            Task t2 = Task.Run(() =>
            {
                Console.WriteLine("Task--2启动...");
            });

            //调用WaitAll() ,会阻塞调用线程,等待任务执行完成 ,这时异步也没有意义了          
            Task.WaitAll(new Task[] { t1, t2 });
            Console.WriteLine("Task完成...");

Task等待任务结果,处理结果

Task.ContinueWith

          Task t1 = Task.Run(() =>
            {
                Console.WriteLine("Task-1启动...");
            });
            Task t2 = Task.Run(() =>
            {
                Console.WriteLine("Task-2启动...");
            });
            //调用WaitAll() ,会阻塞调用线程,等待任务执行完成 ,这时异步也没有意义了          
            Task.WaitAll(new Task[] { t1, t2 });
            Console.WriteLine("Task完成...");

            //调用ContinueWith,等待任务完成,触发下一个任务,这个任务可当作任务完成时触发的回调函数。
            //为了获取结果,同时不阻塞调用线程,建议使用ContinueWith,在任务完成后,接着执行一个处理结果的任务。
            t1.ContinueWith((t) =>
            {
                Console.WriteLine("Task-1完成...");
            });
            t2.ContinueWith((t) =>
            {
                Console.WriteLine("Task-2完成...");
            });

Task.GetAwaiter().OnCompleted


            Task t1 = Task.Run(() =>
            {
                Console.WriteLine("Task-1启动...");
            });
            Task t2 = Task.Run(() =>
            {
                Console.WriteLine("Task-2启动...");
            });
            //调用WaitAll() ,会阻塞调用线程,等待任务执行完成 ,这时异步也没有意义了          
            Task.WaitAll(new Task[] { t1, t2 });
            Console.WriteLine("Task完成...");
             //调用GetAwaiter()方法,获取任务的等待者,调用OnCompleted事件,当任务完成时触发
            //调用OnCompleted事件也不会阻塞线程
            t1.GetAwaiter().OnCompleted(() =>
            {
                Console.WriteLine("Task-1完成...");
            });
            t2.GetAwaiter().OnCompleted(() =>
            {
                Console.WriteLine("Task-2完成...");
            });

Task任务取消

         //实例化一个取消实例
            var source = new CancellationTokenSource();
            var token = source.Token;

            Task t1 = Task.Run(() =>
            {
                Thread.Sleep(2000);
                //判断是否任务取消
                if (token.IsCancellationRequested)
                {
                    // 调用该方法后线程状态才会变为Canceled状态
                    token.ThrowIfCancellationRequested();
                    //Console.WriteLine("任务已取消");
                }
                Thread.Sleep(500);
                //token传递给任务
            }, token);

            Thread.Sleep(1000);
            Console.WriteLine(t1.Status);
            Thread.Sleep(1000);
            //取消该任务
            source.Cancel();
            Thread.Sleep(1000);
            Console.WriteLine(t1.Status);
            Console.ReadKey();

Task返回值

       public static void Test6()
        {
            Task<string> t1 = Task.Run(() => Tttst("HELLOW"));
            t1.Wait();//阻塞,等待结果
            Console.WriteLine(t1.Result);
        }
        static string Tttst(string STR)
        {
            return STR + "  FROM TEST";
        }

Async await

异步方法执行流程:
主线程调用MethodAsync方法,并等待方法执行结束
异步方法开始执行,输出“异步执行”
异步方法执行到await关键字,此时MethodAsync方法挂起,等待await表达式执行完毕,同时将控制权返回给调用方主线程,主线程继续执行。
执行Task.Delay方法,同时主线程继续执行之后的方法。
Task.Delay结束,await表达式结束,MehtodAsync执行await表达式之后的语句,输出“异步执行结束”。

       public static async void MethodAsync()
         {
            Console.WriteLine("异步执行");
            await Task.Delay(1000);
            Console.WriteLine("异步执行结束");
        }

在这里插入图片描述

Task.Delay与Thread.Sleep

Thread.Sleep会阻塞线程。而Task.Delay创建一个将在设置时间后执行的任务,就相当于一个定时器,多少时间后再执行操作,不会阻塞执行线程。

     public static async Task Test7Async()
        {
            Console.WriteLine("await执行前..." + Thread.CurrentThread.ManagedThreadId);
            await Task.Run(() =>
            {
                Console.WriteLine("await执行..." + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                Console.WriteLine("await执行结束..." + Thread.CurrentThread.ManagedThreadId);

            });
            Thread.Sleep(2000);
            Console.WriteLine("await之后执行..." + Thread.CurrentThread.ManagedThreadId);

        }

在这里插入图片描述

并行库Parallel类

该类位于System.Threading.Tasks名称空间中,提供了数据和任务并行性。

Parallel.Invoke

如果多个任务并行运行,可以使用Parallel.Invoke方法。该方法允许传递一个Action委托数组。

 /// <summary>
        /// 并行执行多个任务
        /// </summary>
        public static void ParalelDemo1()
        {
            // 通过Paralel类对象来并行执行3个任务
            Action task1 = () =>
            {
                for (int i = 0; i < 1000000; i++)
                {
                    Console.WriteLine($"1111111i=" + (i * 4));
                }
            };

            Action task2 = () =>
            {
                for (int i = 0; i < 1000000; i++)
                {
                    Console.WriteLine($"222222i=" + (i * 4));
                }
            };

            Action task3 = () =>
            {
                for (int i = 0; i < 1000000; i++)
                {
                    Console.WriteLine($"33333333i=" + (i * 4));
                }
            };

            // 并行执行
            Parallel.Invoke(task1, task2, task3);
        }

Parallel.For

Parallel.For()方法类似C#语法的for循环语句,多次执行一个任务。但该方法并行运行迭代,迭代的顺序没有定义。

Parallel.For()方法中,前两个参数定义了循环的开头和结束,第三个参数是一个Action委托。Parallel.For方法返回类型是ParallelLoopResult结构,它提供了循环是否结束的信息。

Parallel.For有多个重载版本和多个泛型重载版本。

/// <summary>
        /// 并行For循环
        /// </summary>
        public static void ParallelDemo2()
        {
            ParallelLoopResult plr = Parallel.For(0, 100, i =>
            {
                Console.WriteLine("{0},task:{1},thread:{2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
            });
            if (plr.IsCompleted)
            {
                Console.WriteLine("completed!");
            }
         }

在这里插入图片描述

停止Parallel.For

 ParallelLoopResult plr = Parallel.For(0, 100, (int i, ParallelLoopState pls) =>
            {
                Console.WriteLine("{0},task:{1},thread:{2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
                if (i > 5) pls.Break();
            });
            Console.WriteLine("is completed:{0}", plr.IsCompleted);
            Console.WriteLine("最低停止索引:{0}", plr.LowestBreakIteration);

Parallel.ForEach

Parallel.ForEach方法遍历实现了IEnumerable的集合,类似于foreach,但以异步方式遍历。没有确定遍历顺序。

  string[] data = {
                "zero",
                "one",
                "two",
                "three",
                "four",
                "five",
                "six",
                "seven",
                "eight",
                "nine",
                "ten",
                "eleven",
                "twelve"
            };

            ParallelLoopResult plr = Parallel.ForEach<string>(data, s =>
            {
                Console.WriteLine(s);
            });
            if (plr.IsCompleted)
                Console.WriteLine("completed!");

在这里插入图片描述

Task 异步实例

同步与异步区别

同步:

       public static   void LongProcess()
        {
            Console.WriteLine("LongProcess===>批量读取2000个点位数据");
            //模拟读取数据需要时间5秒
             Thread.Sleep(5000);//会阻塞线程
           
            Console.WriteLine("LongProcess===>批量读取数据结束。。。");
        }
        public static void ShortProcess()
        {
            Console.WriteLine("ShortProcess===>批量读取200个点位数据");
            //模拟读取数据需要时间5秒
            Thread.Sleep(5000);
            Console.WriteLine("ShortProcess===>批量读取数据结束。。。");
        }

在这里插入图片描述
异步:

       public static async void LongAsyncProcess()
        {
            Console.WriteLine("LongProcess===>Async批量读取2000个点位数据");
            //模拟读取数据需要时间5秒 
            await Task.Delay(5000);
            Console.WriteLine("LongProcess===>Async批量读取数据结束。。。");
        }
        public static void ShortProcess()
        {
            Console.WriteLine("ShortProcess===>批量读取200个点位数据");
            //模拟读取数据需要时间5秒
            Thread.Sleep(5000);
            Console.WriteLine("ShortProcess===>批量读取数据结束。。。");
        }

在这里插入图片描述

异步方法

不带返回值的异步方法

       public async static void TestTask()
        {
            await Task.Run(() => { });
        }
        public async static Task TestTask1()
        {
            await Task.Run(() => { });
        } 

带返回值的异步方法与调用方法

         /// <summary>
        /// 异步方法调用
        /// </summary>
        public static async void TestASYNC()
        {
            //方法一 异步执行
            Task<int> result = LongProcesss();
            var var2 = await result;
            方法二 阻塞当前线程
            var var2 = await LongProcesss1(); 
           
            Console.WriteLine($"Result:{var2}");
        }
        /// <summary>
        /// 带返回值的异步方法
        /// </summary>
        /// <returns></returns>
        public async static Task<int> LongProcesss()
        {
            Console.WriteLine("异步线程开始");
            await Task.Delay(5000);
            return 10;
        }

在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内容简介 您想淋漓尽致地发挥多核计算机系统的处理能力吗?《C#并行编程高级教程:精通NET 4 Parallel Extensions》将帮助您实现这一夙愿。这本精品书籍浓墨重彩地描述如何使用C# 4、Visual Studio 2010和.NET Framework 4高效地创建基于任务的并行应用程序,详细讲述最新的单指令、多数据流指令和向量化等并行编程技术,介绍现代并行库,讨论如何珠联璧合地使用高级Intel工具与C#,并指导您巧妙使用新引入的轻型协调结构来开发自己的解决方案并解决最棘手的并发编程问题。 主要内容 ◆介绍如何基于新Task Parallel Library和.NET 4设计稳定的可扩展并行应用程序。 ◆讲解命令式数据并行、命令式任务并行、并发集合以及协调数据结构。 ◆描述PLINQ高级声明式数据并行。 ◆讨论如何使用新的Visual Studio 2010并行调试功能来调试匿名方法、任务和线程。 ◆演示如何对数据源进行分区,以便在不同任务和线程之间合理分配工作负荷。 作者简介 Caston C.Hillar是一位独立软件咨询师,自1997年起便一直从事并行编程、多处理器和多核领域的研究,Gaston拥有使用C#和.NET Framework来设计和开发各种复杂并行解决方案的丰富经验,曾于2009年荣膺Intel Black Belt Software Developer奖。 目录 第1章 基于任务的程序设计 1.1 使用共享内存的多核系统 1.1.1 共享内存多核系统与分布式内存系统之间的区别 1.1.2 并行程序设计和多核程序设计 1.2 理解硬件线程和软件线程 1.3 理解Amdahl法则 1.4 考虑Gustafson法则 1.5 使用轻量级并发模型 1.6 创建成功的基于任务的设计 1.6.1 以并发的思想指导设计 1.6.2 理解交错并发、并发和并行之间的区别 1.6.3 并行化任务 1.6.4 尽量减少临界区 1.6.5 理解多核并行程序的设计原则 1.7 为NUMA架构和更高的可扩展性做好准备 1.8 判断是否适合并行化 1.9 小结 第2章 命令式数据并行 2.1 加载并行任务 2.1.1 System.Threading.Tasks.Parallel类 2.1.2 Parallel.Invoke 2.2 将串行代码转换为并行代码 2.2.1 检测可并行化的热点 2.2.2 测量并行执行的加速效果 2.2.3 理解并发执行 2.3 循环并行化 2.3.1 Parallel.For 2.3.2 Parallel.ForEach 2.3.3 从并行循环中退出 2.4 指定并行度 2.4.1 ParallelOptions 2.4.2 计算硬件线程 2.4.3 逻辑内核并不是物理内核 2.5 通过甘特图检测临界区 2.6 小结 第3章 命令式任务并行 3.1 创建和管理任务 3.1.1 System.Threading.Tasks.Task 3.1.2 理解Task状态和生命周期 3.1.3 通过使用任务来对代码进行并行化 3.1.4 等待任务完成 3.1.5 忘记复杂的线程 3.1.6 通过取消标记取消任务 3.1.7 从任务返回值 3.1.8 TaskCreationOptions 3.1.9 通过延续串联多个任务 3.1.10 编写适应并发和并行的代码 3.2 小结 第4章 并发集合 4.1 理解并发集合提供的功能 4.1.1 System.Collections.Concurrent 4.1.2 ConcurrentQueue 4.1.3 理解并行的生产者-消费者模式 4.1.4 ConcurrentStack 4.1.5 将使用数组和不安全集合的代码转换为使用并发集合的代码 4.1.6 ConcurrentBag 4.1.7 IProducerConsumerCollection 4.1.8 BlockingCollection 4.1.9 ConcurrentDictionary 4.2 小结 第5章 协调数据结构 5.1 通过汽车和车道理解并发难题 5.1.1 非预期的副作用 5.1.2 竞争条件 5.1.3 死锁 5.1.4 使用原子操作的无锁算法 5.1.5 使用本地存储的无锁算法 5.2 理解新的同步机制 5.3 使用同步原语 5.3.1 通过屏障同步并发任务 5.3.2 屏障和ContinueWhenAll 5.3.3 在所有的参与者任务中捕捉异常 5.3.4 使用超时 5.3.5 使用动态数目的参与者 5.4 使用互斥锁 5.4.1 使用Monitor 5.4.2 使用锁超时 5.4.3 将代码重构为避免使用锁 5.5 将自旋锁用作互斥锁原语 5.5.1 使用超时 5.5.2 使用基于自旋的等待 5.5.3 自旋和处理器出让 5.5.4 使用volatile修饰符 5.6 使用轻量级的手动重置事件 5.6.1 使用ManualResetEventSlim进行自旋和等待 5.6.2 使用超时和取消 5.6.3 使用ManualResetEvent 5.7 限制资源的并发访问 5.7.1 使用SemaphoreSlim 5.7.2 使用超时和取消 5.7.3 使用 Semaphore 5.8 通过CountdownEvent简化动态fork和join场景 5.9 使用原子操作 5.10 小结 第6章 PLINQ:声明式数据并行 6.1 从LINQ转换到PLINQ 6.1.1 ParallelEnumerable及其AsParallel方法 6.1.2 AsOrdered和orderby子句 6.2 指定执行模式 6.3 理解PLINQ中的数据分区 6.4 通过PLINQ执行归约操作 6.5 创建自定义的PLINQ聚合函数 6.6 并发PLINQ任务 6.7 取消PLINQ 6.8 指定所需的并行度 6.8.1 WithDegreeOfParallelism 6.8.2 测量可扩展性 6.9 使用ForAll 6.9.1 foreach和ForAll的区别 6.9.2 测量可扩展性 6.10 通过WithMergeOptions配置返回结果的方式 6.11 处理PLINQ抛出的异常 6.12 使用PLINQ执行MapReduce算法 6.13 使用PLINQ设计串行多步操作 6.14 小结 第7章 Visual Studio 2010的任务调试能力 7.1 充分利用多显示器的支持 7.2 理解并行任务调试器窗口 7.3 查看Parallel Stacks图 …… 第8章 线程池 第9章 异步编程模型 第10章 并行测试和调优 第11章 向量化、SIMD指令以及其他并行库 附录A .NET 4中与并行相关的类图 附录B 并发UML模型 附录C Parallel Extensions Extras
要学好 C#异步编程,以下是一些建议: 1. 理解异步编程的概念:异步编程是一种并发编程的方式,可以提高程序的性能和响应能力。了解异步编程的基本原理和概念,包括异步方法、异步委托、任务等。 2. 使用 async 和 await 关键字:C# 提供了 async 和 await 关键字,用于简化异步编程。学习如何使用这些关键字来定义和调用异步方法,以及如何处理异步操作的结果。 3. 掌握 TaskTask<T> 类:TaskC# 中用于表示异步操作的类,Task<T> 则是带有返回值的异步操作类。了解如何创建、启动和等待任务,以及如何处理任务的结果和异常。 4. 熟悉异步编程模式(APM)和任务并行库(TPL):C# 提供了 APM 和 TPL 两种常用的异步编程模式。学习如何使用 BeginXXX 和 EndXXX 方法来实现 APM,以及如何使用 Parallel 类和并行 LINQ(PLINQ)来实现 TPL。 5. 异常处理和取消操作:在异步编程中,异常处理和取消操作非常重要。学习如何捕获和处理异步操作中可能出现的异常,并了解如何使用 CancellationToken 来取消异步操作。 6. 调试和性能优化:异步编程可能带来一些调试和性能上的挑战。学习如何调试异步代码,并使用适当的技术和工具来优化异步操作的性能。 7. 实践和项目经验:除了理论知识,实践和项目经验也是学习异步编程的关键。尝试编写一些实际的异步代码,并参与或贡献到使用异步编程的项目中,以加深对异步编程的理解和应用。 最重要的是,要不断练习和深入学习,通过阅读相关文档、书籍和参与社区讨论来扩展自己的知识。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值