1.理解同步编程和异步编程
同步编程:程序按照我们指定的逻辑一步步进行;一个方法完成以后才能执行下一个。
异步编程:当一个程序没有运行完成的时候先去执行另外一个进程,当那个进程执行完毕以后,再回到未执行完的程序的原位置继续完成未完成的操作。
同步过程:按照程序设计逻辑一步步执行
异步过程:一起执行
异步:不等他执行完再去执行点别的,非阻塞式
在wpf里面调用控制台
2.进程和线程的概念
进程:一个程序运行的时候,占用的全部计算资源的总和;
线程:程序执行流的最小单位,任何操作都是由线程来操作的;线程依托于进程存在,一个进程包括多个线程,线程也可以有自己的计算资源。
多线程:多个执行流同时运行。
1.cpu太快,分时间片--上下文切换(加载环境--计算--保存环境)
2.多cpu多核,可以独立工作;
异步多线程:多个thread并发;
异步调用不卡界面。
主线程(UI)UI线程维护界面,主线程忙于计算,不卡界面,异步:方法运行的方式和概念
3.异步和多线程
同步:一个执行计算完了才会进行下一个。
异步调用和同步调用的区别
在wpf中维护主界面(UI)的叫主线程;
添加线程:主线程结束以后才可以执行子线程Thread.start();
开辟额外的线程
异步:当完成一个非常需要耗时长的动作时使用异步方法可以让我们在执行过程中不会阻塞线程可以去执行下其他的。
Thread中两个重要方法join and sleep
对于join方法意味着当子线程结束以后才会调用子线程以后的代码。功能:就是可以让子线程在主线程前面执行。进程-------主线程UI。
异步编程中两个重点async和await,一个核心:Task Task<T> (返回的是对象) void
主线程Task返回的是一个对象,sometask对象进行阻塞线程,把这个事情弄完后才会进行接下来的线程。
await的应用位置
- 你await的表达式通常是一个task
- 也可以满足下列条件的任意对象:
有GetAwaiter方法,它返回一个awaiter(实现了INotifyCompletion.OnCompleted接口)
返回适当类型的GetResult方法
一个bool类型的IsCompleted属性
概念:异步方法的控制流
在异步方法async中必须至少有一个await,在await之前的代码允许包含少量且并不耗时的代码;
await行表示即将被执行异步操作的代码;
后续部分:在await表达式之后出现方法的其余代码
需要返回对象还是直接await等待异步执行的结果直接返回
await直接拿到异步方法结果,等待执行结果。
返回对象利用result属性得到结果
Task的四种调用方式
1.Task.Run()
此方法应用的是Task的静态方法,此方法不需要实例化Task,可以直接使用Action进行传参。
2.实例化Task,调用start()方法。
这里也可以直接传入异步方法名,不使用委托;
3.Task.Factory.StartNew()方法启动
使用TaskFactory工厂的StartNew方法启动,其中TaskFactory工厂可以直接实例化,或者 Task.Factory (推荐)。
当前面对象带有返回值的时候调用变量的result属性可以得到异步操作以后我们所需要的结果。
在新的异步方法里面有异步执行。await是等待异步执行的意思.
由于method3需要等待拿到method1的结果,因此执行method3之前需要await等待拿到await1的结果。
C#中如何使用异步lambda表达式来初始化委托实例
Func<Task<T>> Func<Task> Action
async只能修饰方法包括lambda表达式。
在执行异步操作的时候有一对类可以实现,分别是CancellationTokenSource和CancellationToken类
cancellationtoken本身不会让让其停下来,但是cancellationtokensource里面的cancel方法会改变他的状态让异步操作停下来。
在调用方法中同步的去等待任务利用task.wait();
异常处理和await表达式
在这段代码中虽然await方法里面抛出了一个异常,但是异步任务并没有因此而停止,总结原因可能有两类:(1)task异步方法没有被取消;(2)没有未处理的异常。
Task里面的两个静态方法:waitall和waitany方法
不调用两种静态方法
调用静态方法waitall以后等待所有异步操作完成以后才会继续执行,并且显示状态为”Yes“
waitany
此时有且只有一个异步任务完成了。
同步等待与异步等待的区别(异步中的等待--whenall和whenany)
主线程没有完成但是异步完成了,这就是异步中的等待,过程不会阻塞主线程
whenall的作用
whenany的效果
await Task.delay()和Thread.Sleep()
前者不会阻塞线程,后者会阻塞线程;前者是同步延迟,后者是异步延迟;前者可以取消,后者不可以取消。下图为对比:
异步编程在GUI上的运用
Task.delay的使用
运用这个方法不会阻塞线程,就是避免了卡界面的问题。
Task.Yield的使用
碰到这个Task.Yield以后就会把进入此时正在执行的程序从队列里面拿下来,然后转到执行其他的任务;
关于一个完整的异步GUI程序
这个程序包括一个进度条和两个按钮,当进程正常完成时显示如下:
当中途取消异步任务时是这个样子;
关于BackWorker类的使用
方法与事件的结合,采用后台线程,另建一个线程来执行任务;
并行循环(parellel.for和parellel.foreach)
特点:并行迭代;
两者的使用方式不一样;但是在第三个参数的地方均使用了lambda表达式。
委托中的异步调用(BeginInvoke和EndInvoke)
调用begininvoke是从线程池中调出一个线程来执行委托的方法,有三个参数包括引用的目标方法的参数类型(想要得到返回的参数使用IAsyncResult方法得到),一个是回调,一个是state;
endinvoke是专门用来接受BeginIInvoke的Iasyncresult对象的,并且提取出所有的返回结果给一个变量。
当调用begininvoke方法的时候系统创建了一个AsyncResut对象,但是它并不返回类对象的引用,而是返回了类对象中的IAsyncResult接口,里面有一个IsCompelete属性用来判断异步方法是否完成。
轮询模式
在调用begininvoke以后做轮询处理,检查异步操作是否完成,未完成就进入while循环。执行其他操作。
回调模式(区别:相当于在异步方法完成之后,进入回调方法处理数据得到想输出的数据类型)
特指BeginInvoke方法中的第二个参数和第三个参数,第三个参数是准备传入回调方法的对象,可以为null;
计时器(Timer)
利用Timer,其中Timer的第一个参数是回调方法,第二个参数是传入的参数,第三个参数是第一次任务开始执行的时间,第四个参数是每次执行时间间隔。
完结!!!
附录:Task编程中常出现的问题
1.请考虑将await关键字应用于调用结果(参考文章:请考虑将 "await" 运算符应用于调用结果 - 风可以追我 - 博客园 (cnblogs.com)https://www.cnblogs.com/lnwuyaowei/p/12672866.html)
1、不使用await关键字,在异步线程中,由于可能没有足够的时间等待返回结果,所以可能不会在预期的代码段上取得正确的结果。
2、Method1使用await关键字异步执行,Method2为不涉及异步执行的方法,会在Method2处阻塞主线程。
3、都使用await关键字,在异步线程中,异步方法是按队列执行的,结果与同步方法一样。好处是没有阻塞主线程执行其它任务。
所以在asp.net core中的代码:
await TryUpdateModelAsync
await _context.SaveChangesAsync()
是顺序执行的,后面的代码无论怎么写,都必然是顺序执行。
但有一点除外,那就是:同步调用异步方法,也就是异步方法前不加await关键字进行调用,这时程序的运行结果可能会变得非常诡异。
把调用异步方法和等待最终返回结果分别设计
这参数后面有取消标记。
task任务本身带不了参数但是委托方法可以,task只可以传action委托带func的返回;async方法里面一定要有await标识符不然即使有了async也是同步执行.
await的两重含义:1.跳出异步方法;2.等待拿到结果。
关于Task:是在基于ThreadPool(线程池的基础上)
Task确实是一个很神秘的东西,但从根本上说,其实就是开启线程上的任务,即:允许多个任务可以有多个线程(或在同一线程内)进行,不必等到上一个任务执行完,才开始执行下一个任务,很直观的说,就是我在跑步的同时,不影响我也在听音乐!就好像,我在运行QQ的时候,不影响酷狗也在播放音乐!就比如下面的代码,我在比较值的同时,同时也在对该值得进行输出,二者同时进行!
从线程池拿取一个线程从而开启新的线程。不和主线程争抢;先打印主线程,说明创建的task不和主线程进行争抢。
在task异步里面执行同步方法
异步和Task(多任务)本身就是两个不同的概念,异步是同时执行多个任务,而Task则是允许多个任务可以在线程内同时进行,多任务的同时进行,则是由异步来进行,而异步方法,必须为Task<T>
加了await以后存在线程等待要等待上一个线程结束以后才能开始执行下一个线程;异步同步调用;异步方法结合task
对于Thread方法里面的阻塞线程Join缺点:每次想阻塞一次线程就需要调用一下这个方法,而Task则提供了很多可以方便控制线程的方式。(有些新的就会补充)