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

简介: 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 一个工厂对象,可创建多种 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键,模拟失败:
shucr


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

相关文章
|
2月前
|
数据采集 XML JavaScript
C# 中 ScrapySharp 的多线程下载策略
C# 中 ScrapySharp 的多线程下载策略
|
24天前
|
设计模式 缓存 Java
谷粒商城笔记+踩坑(14)——异步和线程池
初始化线程的4种方式、线程池详解、异步编排 CompletableFuture
谷粒商城笔记+踩坑(14)——异步和线程池
|
2月前
|
缓存 Java
异步&线程池 线程池的七大参数 初始化线程的4种方式 【上篇】
这篇文章详细介绍了Java中线程的四种初始化方式,包括继承Thread类、实现Runnable接口、实现Callable接口与FutureTask结合使用,以及使用线程池。同时,还深入探讨了线程池的七大参数及其作用,解释了线程池的运行流程,并列举了四种常见的线程池类型。最后,阐述了在开发中使用线程池的原因,如降低资源消耗、提高响应速度和增强线程的可管理性。
异步&线程池 线程池的七大参数 初始化线程的4种方式 【上篇】
|
2月前
|
Java 数据库
异步&线程池 CompletableFuture 异步编排 实战应用 【终结篇】
这篇文章通过一个电商商品详情页的实战案例,展示了如何使用`CompletableFuture`进行异步编排,以解决在不同数据库表中查询商品信息的问题,并提供了详细的代码实现和遇到问题(如图片未显示)的解决方案。
异步&线程池 CompletableFuture 异步编排 实战应用 【终结篇】
|
7天前
|
数据采集 消息中间件 并行计算
进程、线程与协程:并发执行的三种重要概念与应用
进程、线程与协程:并发执行的三种重要概念与应用
20 0
|
1月前
|
C# 数据安全/隐私保护
C# 一分钟浅谈:类与对象的概念理解
【9月更文挑战第2天】本文从零开始详细介绍了C#中的类与对象概念。类作为一种自定义数据类型,定义了对象的属性和方法;对象则是类的实例,拥有独立的状态。通过具体代码示例,如定义 `Person` 类及其实例化过程,帮助读者更好地理解和应用这两个核心概念。此外,还总结了常见的问题及解决方法,为编写高质量的面向对象程序奠定基础。
17 2
|
19天前
|
安全 数据库连接 API
C#一分钟浅谈:多线程编程入门
在现代软件开发中,多线程编程对于提升程序响应性和执行效率至关重要。本文从基础概念入手,详细探讨了C#中的多线程技术,包括线程创建、管理及常见问题的解决策略,如线程安全、死锁和资源泄露等,并通过具体示例帮助读者理解和应用这些技巧,适合初学者快速掌握C#多线程编程。
53 0
|
2月前
|
存储 监控 Java
|
2月前
|
Java
异步&线程池 CompletableFuture 异步编排 【下篇】
这篇文章深入探讨了Java中的`CompletableFuture`类,解释了如何创建异步操作、使用计算完成时的回调方法、异常处理、串行化方法、任务组合以及多任务组合的使用方式,并通过代码示例展示了各种场景下的应用。
异步&线程池 CompletableFuture 异步编排 【下篇】