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月前
|
编解码 数据安全/隐私保护 计算机视觉
Opencv学习笔记(十):同步和异步(多线程)操作打开海康摄像头
如何使用OpenCV进行同步和异步操作来打开海康摄像头,并提供了相关的代码示例。
122 1
Opencv学习笔记(十):同步和异步(多线程)操作打开海康摄像头
|
5天前
|
存储 安全 编译器
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
37 12
|
24天前
|
调度 开发者
核心概念解析:进程与线程的对比分析
在操作系统和计算机编程领域,进程和线程是两个基本而核心的概念。它们是程序执行和资源管理的基础,但它们之间存在显著的差异。本文将深入探讨进程与线程的区别,并分析它们在现代软件开发中的应用和重要性。
44 4
|
1月前
|
C# UED SEO
C# 异步方法async / await任务超时处理
通过使用 `Task.WhenAny`和 `Task.Delay`方法,您可以在C#中有效地实现异步任务的超时处理机制。这种方法允许您在指定时间内等待任务完成,并在任务超时时采取适当的措施,如抛出异常或执行备用操作。希望本文提供的详细解释和代码示例能帮助您在实际项目中更好地处理异步任务超时问题,提升应用程序的可靠性和用户体验。
73 3
|
2月前
|
安全 调度 C#
STA模型、同步上下文和多线程、异步调度
【10月更文挑战第19天】本文介绍了 STA 模型、同步上下文和多线程、异步调度的概念及其优缺点。STA 模型适用于单线程环境,确保资源访问的顺序性;同步上下文和多线程提高了程序的并发性和响应性,但增加了复杂性;异步调度提升了程序的响应性和资源利用率,但也带来了编程复杂性和错误处理的挑战。选择合适的模型需根据具体应用场景和需求进行权衡。
|
2月前
|
网络协议 安全 Java
难懂,误点!将多线程技术应用于Python的异步事件循环
难懂,误点!将多线程技术应用于Python的异步事件循环
86 0
|
3月前
|
设计模式 缓存 Java
谷粒商城笔记+踩坑(14)——异步和线程池
初始化线程的4种方式、线程池详解、异步编排 CompletableFuture
|
3月前
|
数据采集 消息中间件 并行计算
进程、线程与协程:并发执行的三种重要概念与应用
进程、线程与协程:并发执行的三种重要概念与应用
77 0
|
3月前
|
安全 数据库连接 API
C#一分钟浅谈:多线程编程入门
在现代软件开发中,多线程编程对于提升程序响应性和执行效率至关重要。本文从基础概念入手,详细探讨了C#中的多线程技术,包括线程创建、管理及常见问题的解决策略,如线程安全、死锁和资源泄露等,并通过具体示例帮助读者理解和应用这些技巧,适合初学者快速掌握C#多线程编程。
88 0