一,相关关键字和运算符
1.1 Async/Await 介绍和使用示例
- 关键字 Async
使用 'async' 修饰符可将方法、lambda 表达式或匿名方法指定为异步。 如果对方法或表达式使用此修饰符,则其称为异步方法 。'async' 关键字是上下文关键字,原因在于只有当它修饰方法、lambda 表达式或匿名方法时,它才是关键字。
如果 'async' 关键字修改的方法不包含 'await' 表达式或语句,则该方法将同步执行。 编译器警告将通知你不包含 'await' 语句的任何异步方法,因为该情况可能表示存在错误。
定义异步方法:
public async Task<string> AsyncTest()
{
}
- 运算符 Await
'await' 运算符暂停对其所属的 'async' 方法的求值,直到其操作数表示的异步操作完成。 异步操作完成后,如果有返回值 'await' 运算符将返回操。 当 'await' 运算符应用到表示已完成操作的操作数时,它将立即返回操作的结果,而不会暂停其所属的方法。 'await' 运算符不会阻止计算异步方法的线程。 当 'await' 运算符暂停其所属的异步方法时,控件将返回到方法的调用方法。
使用Await运算符:
public async Task<string> AsyncTest()
{
// 等AsyncTest_1 执行完成
await AsyncTest_1();
}
public async Task<string> AsyncTest_1()
{
return "任务AsyncTest_1 执行完成";
}
1.2 Async/Await 异步编程中的最佳做法
此部分(1.3)取自 --> MSDN
二, Task 类
2.1 Task定义
Task类表示不返回值并且通常以异步方式执行的单个操作。 Task 对象是在 .NET Framework 4 中首次引入的 基于任务的异步模式 的中心组件之一。 由于对象执行的工作 Task 通常在线程池线程上异步执行,而不是在主应用程序线程上同步执行,因此可以使用 Status 属性以及 IsCanceled 、 IsCompleted 和 IsFaulted 属性来确定任务的状态。 通常,lambda 表达式用于指定任务要执行的工作。
2.2 属性方法
属性列表:
属性名 | 说明 |
---|---|
AsyncState | 获取在创建 Task 时提供的状态对象,如果未提供,则为 null。(只读) |
CompletedTask | 此属性将返回其 Status 属性设置为的任务 RanToCompletion 。 若要创建一个返回值并运行到完成的任务,请调用 FromResult 方法。(只读) |
CreationOptions | 获取用于创建此任务的 TaskCreationOptions 。(只读) |
CurrentId | 返回当前正在执行 Task 的 ID。(只读) |
Exception | 获取导致 AggregateException 提前结束的 Task 。 如果 Task 成功完成或尚未引发任何异常,这将返回 null。(只读) |
Factory | 一个工厂对象,可创建多种 Task 和 Task<TResult> 对象。提供对用于创建和配置 Task 和 Task 实例的工厂方法的访问。 |
Id | 任务 Id 按需分配,不一定表示任务实例的创建顺序,有可能存在冲突。若要从任务正在执行的代码内获取当前正在执行的任务的任务 ID,请使用 CurrentId 属性。 |
IsCanceled | 如果任务由于被取消而完成,则为 true;否则为 false。 |
IsCompleted | true 如果任务已完成 (即,任务处于以下三个最终状态之一: RanToCompletion 、 Faulted 或 Canceled ) ,则为; 否则为 false 。 |
IsCompletedSuccessfully | true 如果任务运行到完成,则为;否则为 false 。 |
IsFaulted | 如果任务引发了未经处理的异常,则为 true;否则为 false。 |
Status | 此任务实例的当前 TaskStatus 。 |
方法列表:
方法名 | 方法说明 |
---|---|
ConfigureAwait(bool) | 参数:尝试将延续任务封送回上下文,则为 true;否则为 false。 返回值:尝试将延续任务封送回原始上下文,则为 true;否则为 false。 |
ContinueWith() | 创建一个在目标 Task 完成时异步执行的延续任务。即完成一个任务开启下一个任务。 |
Delay() | 创建将在时间延迟后完成的任务。在完成返回的任务前要等待的参数毫秒数;如果无限期等待,则为 -1。 |
Dispose() | 释放 Task 类的当前实例所使用的所有资源。 |
FromCanceled() | 创建 Task 或者 Task<TResult> ,它因指定的取消标记进行的取消操作而完成。 |
FromException() | 创建 Task 或者 Task<TResult> ,它在完成后出现指定的异常。 |
FromResult(TResult) |
类型参数: TResult 任务返回的结果的类型。参数 TResult 存储入已完成任务的结果。返回值: Task<TResult> 已成功完成的任务。 |
GetAwaiter() | 获取用于等待此 Task 的 awaiter 。返回:一个 awaiter 实例。 |
Run() | 将在线程池上运行的指定工作排队,并返回该工作的任务或 Task<TResult> 句柄。 |
RunSynchronously() | 对当前的 Task 同步运行 TaskScheduler 。 |
Start() | 启动 Task ,并将它安排到当前的 TaskScheduler 中执行。 |
Wait() | 等待当前 'Task' 完成执行过程。 |
WaitAll() | 等待所有提供的 Task 对象完成执行过程。 |
WaitAny() | 等待提供的任一 Task 对象完成执行过程。 |
WhenAll() | 所有提供的任务已完成时,创建将完成的任务。 |
WhenAny() | 任何提供的任务已完成时,创建将完成的任务。 |
Yield() | 创建异步产生当前上下文的等待任务。可以 await Task.Yield(); 在异步方法中使用来强制异步完成方法。 |
1.3 Task使用
Task最厉害的地方就是他的任务控制了,你可以很好的控制task的执行顺序,让多个task有序的工作。
任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制。
一个最简单的示例: 开启任务1 --> 调用并等待任务2完成 --> 继续执行任务1
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskTest
{
class Program
{
static void Main(string[] args)
{
Task<string> result = task_1();
Console.WriteLine(result.Result);
Console.ReadKey();
}
static async Task<string> task_1()
{
Console.WriteLine("任务1 开始执行");
Console.WriteLine("等待任务2 执行完成... ");
string res = await task_2();
Console.WriteLine("任务2 执行完成... 返回值: " + res);
return "任务1执行完成返回值";
}
static async Task<string> task_2()
{
Thread.Sleep(1000);
return "任务2执行完成";
}
}
}
执行结果:
三,TaskCompletionSource 类
3.1 概念定义
TaskCompletionSource :表示未绑定到委托的 Task 的制造者方,并通过 Task 属性提供对使用者方的访问。
通常情况下, Task
需要表示另一个异步操作。 TaskCompletionSource
提供此目的。 它允许创建可以向使用者传递的任务,而这些使用者可以使用任务的成员,就像对待任何其他成员一样。 但是,与大多数任务不同,由创建的任务的状态由 TaskCompletionSource
中的方法显式控制 TaskCompletionSource
。 这使得外部异步操作能够传播到基础 Task
。 分隔还可确保使用者不能在不访问相应的的情况下转换状态 TaskCompletionSource
。所有成员 TaskCompletionSource
都是线程安全的,可同时从多个线程使用。
3.2 属性函数
属性:
- Task :获取由此 'Task' 创建的 'TaskCompletionSource'。
此属性使使用者可以访问由此 'Task' 实例控制的。 'SetResult()' 'SetException(Exception)' 'SetException(IEnumerable)' 此实例上的 和 'SetCanceled()' 方法 (及其 Try 变体) 都将导致相关状态在此基础上转换 Task 。
构造函数:
函数 | 说明 |
---|---|
TaskCompletionSource() | 创建一个 'TaskCompletionSource'。 |
TaskCompletionSource(Object) | 使用指定的状态创建一个 'TaskCompletionSource'。 |
TaskCompletionSource(Object, TaskCreationOptions) | 使用指定的状态和选项创建一个 'TaskCompletionSource'。'Task'通过此实例创建并可通过其属性访问的将 'Task' 使用指定的实例化 'creationOptions'。 |
TaskCompletionSource(TaskCreationOptions) | 使用指定的选项创建一个 'TaskCompletionSource'。 |
函数:
PS:基础 Task 已处于以下三种最终状态的其中一种:RanToCompletion、Faulted 或 Canceled。
函数名 | 说明 |
---|---|
SetCanceled() | 将基础 'Task' 转换为 'Canceled' 状态。 |
SetCanceled(CancellationToken) | 使用指定的标记将基础 'Task' 转换为 'Canceled' 状态。 |
SetException(Exception) | 将基础 'Task' 转换为 'Faulted' 状态。 |
SetException(IEnumerable) | 将基础 'Task' 转换为 'Faulted' 状态。 |
SetResult() | 将基础 'Task' 转换为 'RanToCompletion' 状态。 |
TrySetCanceled() | 尝试将基础 'Task' 转换为 'Canceled' 状态。 |
TrySetCanceled(CancellationToken) | 尝试将基础 'Task' 转换为 'Canceled' 状态。 |
TrySetException(Exception) | 尝试将基础 'Task' 转换为 'Faulted' 状态。 |
TrySetException(IEnumerable) | 尝试将基础 'Task' 转换为 'Faulted' 状态。 |
TrySetResult() | 尝试将基础 'Task' 转换为 'RanToCompletion' 状态。 |
3.3 模拟情景
用户操作:
- 用户点击商品 --> 选择支付平台 --> 等待选择完成 --> 执行支付逻辑
代码逻辑:
- 调用支付任务 --> 等待用户选择 --> 返回用户所选 --> 执行支付逻辑
using System;
using System.Threading.Tasks;
namespace VSProject
{
class Program
{
static TaskCompletionSource<string> ts;
static void Main(string[] args)
{
// 开启异步任务
TaskTest_1();
while (true)
{
ConsoleKeyInfo info = Console.ReadKey(true);
switch (info.Key)
{
case ConsoleKey.S:
Console.WriteLine("用户输入S键,模拟任务成功");
Success();
break;
case ConsoleKey.F:
Console.WriteLine("用户输入F键,模拟任务成功");
Fail();
break;
}
}
Console.ReadLine();
}
/// <summary>
/// 异步任务
/// </summary>
/// <returns></returns>
static async Task TaskTest_1()
{
Console.WriteLine("TaskTest_1 任务开始...");
ts = new TaskCompletionSource<string>();
string res;
try
{
res = await ts.Task;
}
catch (Exception e)
{
Console.WriteLine("等待任务异常...");
throw e;
}
Console.WriteLine("TaskTest_1 任务结束... 传值为:" + res);
}
/// <summary>
/// 任务成功
/// </summary>
static void Success()
{
if (ts.Task.IsCompleted) return;
ts.SetResult("模拟任务完成传值");
}
/// <summary>
/// 任务取消或失败
/// </summary>
static void Fail()
{
if (ts.Task.IsCompleted) return;
ts.SetException(new Exception("任务取消或失败"));
}
}
}
用户按下S键,模拟成功:
用户按下F键,模拟失败: