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 类

相关文章
|
5月前
|
数据采集 存储 JSON
Python爬取知乎评论:多线程与异步爬虫的性能优化
Python爬取知乎评论:多线程与异步爬虫的性能优化
|
2月前
|
设计模式 消息中间件 安全
【JUC】(3)常见的设计模式概念分析与多把锁使用场景!!理解线程状态转换条件!带你深入JUC!!文章全程笔记干货!!
JUC专栏第三篇,带你继续深入JUC! 本篇文章涵盖内容:保护性暂停、生产者与消费者、Park&unPark、线程转换条件、多把锁情况分析、可重入锁、顺序控制 笔记共享!!文章全程干货!
236 1
|
10月前
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
5月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
6月前
|
机器学习/深度学习 监控 算法
局域网行为监控软件 C# 多线程数据包捕获算法:基于 KMP 模式匹配的内容分析优化方案探索
本文探讨了一种结合KMP算法的多线程数据包捕获与分析方案,用于局域网行为监控。通过C#实现,该系统可高效检测敏感内容、管理URL访问、分析协议及审计日志。实验表明,相较于传统算法,KMP在处理大规模网络流量时效率显著提升。未来可在算法优化、多模式匹配及机器学习等领域进一步研究。
186 0
|
10月前
|
缓存 安全 Java
面试中的难题:线程异步执行后如何共享数据?
本文通过一个面试故事,详细讲解了Java中线程内部开启异步操作后如何安全地共享数据。介绍了异步操作的基本概念及常见实现方式(如CompletableFuture、ExecutorService),并重点探讨了volatile关键字、CountDownLatch和CompletableFuture等工具在线程间数据共享中的应用,帮助读者理解线程安全和内存可见性问题。通过这些方法,可以有效解决多线程环境下的数据共享挑战,提升编程效率和代码健壮性。
346 6
|
11月前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
399 17
|
10月前
|
数据采集 Java 数据处理
Python实用技巧:轻松驾驭多线程与多进程,加速任务执行
在Python编程中,多线程和多进程是提升程序效率的关键工具。多线程适用于I/O密集型任务,如文件读写、网络请求;多进程则适合CPU密集型任务,如科学计算、图像处理。本文详细介绍这两种并发编程方式的基本用法及应用场景,并通过实例代码展示如何使用threading、multiprocessing模块及线程池、进程池来优化程序性能。结合实际案例,帮助读者掌握并发编程技巧,提高程序执行速度和资源利用率。
482 0
|
12月前
|
存储 安全 编译器
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
870 12
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
253 12

热门文章

最新文章