异步编程
async/await特性异步编程
- 使用模型:
private await Task<int> YibuAsync(int a)//定义一个异步方法 YibuAsync await关键字指示编译器方法内部可能会存在await表达式
{
//do something
int b = await AnotherAsync(a);//如果只是标记了async关键字,但方法内没有await表达式,方法仍将同步执行
return b;//b实际返回到了Task.Result中
}
Task<int> t = YibuAsync(10);//调用异步方法
//doing something
异步方法:async和await关键字同时存在。
- 控制流程:
-
调用异步方法,调用后立即返回一个Task
类型的对象。 - 调用异步方法后执行到await表达式后返回
- 继续执行调用者后续代码。
- 当需要使用异步方法执行结果时,若异步方法任未返回。将生成一个continue委托,当操作完成的时候调用continue委托。这个continue委托将控制权重新返回到”async”方法对应的await唤醒点处。
- 异步方法返回类型
-
void
调用并返回,调用异步方法后不再做任何处理 -
Task
-
Task<T>
-
ValueTask<T>
-
await表达式
await表达式由await关键字和一个空闲对象组成(任务),这个任务可以是Task类型的对象,也可以不是,默认情况下,这个任务在当前线程上异步执行。空闲对象指awaitable类型的实例。awaitable类型指包含GetAwaiter方法的类型,该方法没有参数,返回一个awaiter类型的对象。awaier类型包含以下成员:
bool IsCompleted{get;}
void OnCompleted(Action);
以及一下成员之一:
void GetResult();
T GetResult();
- Task.Run()方法
- Task.Run的方法签名及返回类型
Run(Action action) 返回类型 Task
Run(Action action,CancellationToken token) 返回类型 Task
Run(Func<TResult> function) 返回类型 Task<TResult>
Run(Func<TResult> functiom,CancellationToken token) 返回类型 Task<TResult>
Run(Func<Task> function) 返回类型 Task
Run(Func<Task> function,CancellationToken token) 返回类型 Task
Run(Func<Task<TResult>> function) 返回类型 Task<TResult>
Run(Func<Task<TResult>> function,CancellationToken token) 返回类型 Task<TResult>
注:Action委托:无参无返回值;Func委托:无参有返回值。
BackgroundWorker类异步编程模式
使用BackgroundWorker类创建一个后台线程,并和主线程通信。该类主要成员如下:
属性:WorkerReportsProgress //设置后台线程是否把它的进度汇报给主线程。
WorkerSupportsCancellation //设置后台线程是否支持从主线程取消。
IsBusy //检查后台线程是否正在运行。
CancellationPending //检查后台线程是否需要被取消。
方法:RunWorkerAsync() //获取后台线程,并执行DoWork事件处理程序
CancelAsync() //把CancellationPending属性设置为True。DoWork事件处理程序需要检查这个属性来决定是否应该停止该处理。
RePortProgress()
事件:DoWork
ProgressChanged
RunWorkerCompleted
控制流程:实例化BackgroundWorker类,创建后台线程,设置后台线程是否向主线程汇报进度属性(WorkerReportsProgress)、后台线程是否支持从主线程取消属性(WorkerSupportsProgress)。在主线程调用RunWorkerAsync()方法,获取后台线程,并触发DOWork事件,执行Dowork事件处理程序,若要向主线程汇报进度,则DoWork事件处理程序调用ReportProgress()方法,触发ProgressChanged事件,主线程可以用附加到ProgressChanged事件上的处理程序。若要取消后台线程的执行,则在主线程中调用CancelSAsync()方法,该方法不会立即取消后台线程的执行,而是将CancellationPending的属性设置为True,后台线程的DoWork事件处理程序需要定期检查CancellationPending的属性,来判断是否需要退出。
任务并行库异步编程模式
Parallel.For循环与Parallel.Foreach循环:许多时候,在使用这两个结构时,每一次迭代都依赖于前一次迭代的计算或行为,但有时候不是,如果迭代之间彼此独立,并且程序运行在多处理机上,那么若能将不同的的迭代放在不同的处理器上进行的话,将受益匪浅。Parallel.For与Parallel.Foreach结构就是这样做的。
这两个结构的形式是包含输入参数的方法。Parallel.For方法有12个重载,其中最简单的签名如下:
public static ParallelLoopResult.For(int fromInclusive,int toExclusive,Action body);
- fromInclusive参数是迭代系列的第一个参数。
- ToExclusive参数是比迭代系列最后一个索引号大1的整数。即index<ToExclusive计算的一样。
-
实例代码
using System.Threading.Tasks;
Parallel.For(0,15,i=>Console.WriteLine($"The square of {i} is {i*i}"));
输出结果为无序的0到15的平方。
另一个并行循环结构是Parallel.Foreach(),该方法有多个重载,其中最简单的如下:
static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source,Action<TSource> body)
- TSource 是集合中对象的类型。
- source 是Tsource对象的集合。
- body是要应用到集合中每一个元素上的Lambda表达式。
BeginInvoke与EndEInvoke异步编程模式
委托有两个方法:BeginInvoke与EndInvoke,当委托对象的方法列表中只有一个方法时,可使用这里两个方法使其在一个独立的线程中异步执行。分为三种模式,分别是等待直到完成模式、轮询模式和回掉模式。
先来介绍委托类型中的BeginInvoke与EndInvoke方法
- BeginInvoke方法
- BeiginInvoke方法的参数组成:引用方法需要的参数、callback、state。
- BeginInvoke从线程池中获取一个线程,并让引用方法在新线程中开始运行。
- BeginInvoke方法返回给调用线程一个实现IAsyncResult接口的对象的引用,这个接口引用包含了在线程池中运行的异步方法的当前状态。
-
代码示例:
IAsyncResult iar = del.BeginInvoke(a,b,null,null);
a,b是委托方法的参数,del是对应的委托实例。
-
EndInvoke方法
该方法获取由异步方法调用返回的值,并且释放线程使用的资源:
- 它接受一个由BeginInvoke方法返回的IAsyncResult对象的引用作为参数,并找到它关联的线程。
- 如果线程池中的线程已经退出,则其清理退出线程的状态并释放其资源,找到引用方法的返回值,并将它作为返回值返回。
- 如果当EndInvoke被调用时,线程池中的线程仍在运行,调用线程就会停止并等待其完成。
-
代码示例:
int result = EndInvoke(iar);
result是异步方法的返回值。
-
等待直到完成模式
在发起异步方法并做了一些其他处理后,就中断等待异步方法完成后再继续。 -
轮询模式
原始线程发起异步方法的调用,做一些其他的处理,并通过定期检查IAsyncResult的IsCompleted属性判断线程是否完成,如果完成,则调用EndInvoke方法,否则,做一些其他处理,间隔一段时间再检查。 -
回调模式
原始线程调用异步线程后不在管了,当异步方法调用结束后,系统在新线程中调用用户自定义的方法来处理结果,并且调用委托的EndInvoke方法。这个用户自定义的方法叫回掉方法。