C# 之 异步多线程任务相关以及概念使用介绍

陈言必行 发表于 2021/11/10 11:40:21 2021/11/10
【摘要】 C#异步多线程Task的介绍和使用,从相关关键字到使用示例,详细解析Task和TaskCompletionSource的使用方法。

一,相关关键字和运算符

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 一个工厂对象,可创建多种 TaskTask<TResult> 对象。提供对用于创建和配置 Task 和 Task<TResult> 实例的工厂方法的访问。
Id 任务 Id 按需分配,不一定表示任务实例的创建顺序,有可能存在冲突。若要从任务正在执行的代码内获取当前正在执行的任务的任务 ID,请使用 CurrentId 属性。
IsCanceled 如果任务由于被取消而完成,则为 true;否则为 false。
IsCompleted true 如果任务已完成 (即,任务处于以下三个最终状态之一: RanToCompletionFaultedCanceled) ,则为; 否则为 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 任务返回的结果的类型。参数 TResult 存储入已完成任务的结果。返回值: Task<TResult>已成功完成的任务。
GetAwaiter() 获取用于等待此 Taskawaiter。返回:一个 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<Exception>)’ 此实例上的 和 ‘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<Exception>) 将基础 ‘Task’ 转换为 ‘Faulted’ 状态。
SetResult() 将基础 ‘Task’ 转换为 ‘RanToCompletion’ 状态。
TrySetCanceled() 尝试将基础 ‘Task’ 转换为 ‘Canceled’ 状态。
TrySetCanceled(CancellationToken) 尝试将基础 ‘Task’ 转换为 ‘Canceled’ 状态。
TrySetException(Exception) 尝试将基础 ‘Task’ 转换为 ‘Faulted’ 状态。
TrySetException(IEnumerable<Exception>) 尝试将基础 ‘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键,模拟失败:
shucr


相关链接:
MSDN - Task 类
MSDN - TaskCompletionSource 类

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请发送邮件至:cloudbbs@huaweicloud.com;如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。