首页 > 编程语言 >C#线程---await

C#线程---await

时间:2024-10-16 17:48:03浏览次数:7  
标签:异步 Task C# await --- 任务 WriteLine async

简介:

前面异步编程的不足:它允 许我们以模块化的方式设计程序,来组合不同的异步操作。遗憾的是:

一: 当阅读此类程序时仍然非常难理解程序的实际执行顺序。在大型程序中将会有许多相互依赖的任务和后续操作,用于运行其他后续操作的后续操作,处理异常的后续操 作,并且它们都出现在程序代码中不同的地方。因此了解程序的先后执行次序变成了一个极具挑战性的问题。

二: 另一个需要关注的问题是,能够接触用户界面控制器的每个异步任务是否得到了正确的同步上下文。程序只允许通过UI线程使用这些控制器,否则将会得到多线程访问异常。说到异常,我们不得不使用单独的后续操作任务来处理在之前的异步操作中发生的错 误。这又导致了分散在代码的不同部分的复杂的处理错误的代码,逻辑上无法相互关联:

await的作用:为了解决这些问题,C# 5.0引入了新的语言特性,称为异步函数(asynchronous function)它是TPL之上的更高级别的抽象,真正简化了异步编程。正如在第4章提到的, 抽象隐藏了主要的实现细节,使得程序员无须考虑许多重要的事情,从而使异步编程更容 易。

三: async:要创建一个异步函数,首先需要用async关键字标注一个方法。如果不先做这个,就不可能拥有async属性或事件访问方法和构造函数。异步函数必须返回Task或Task<T>类型 可以使用async void 方法,但是更推荐使用async Task方法。使用async void方法唯一合理的地方是在程序中使用顶层UI控制器事件处理器的时候

async Task<string> GetStringAsync()
(
await Task.Delay(TimeSpan.FromSeconds(2));
return *'Hello/ World!n ;
}

四:使用async关键字标注的方法内部,可以使用await操作符。该操作符可与TPL的任务 一起工作,并获取该任务中异步操作的结果。在本章中稍后会讲述细节。在async方法外不 能使用await关键字,否则会有编译错误。另外,异步函数在其代码中至少要拥有一个await 操作符。然而,如果没有只会导致编译警告,而不是编译错误。

五:需要注意的是,在执行完await调用的代码行后该方法会立即返回。如果是同步执 行,执行线程将会阻塞两秒然后返回结果-这里当执行完await操作后,立即将工作者线程放回线程池的过程中,我们会异步等待。2秒后,我们又一次从线程池中得到工作者线 程并继续运行其中剩余的异步方法. 这允许我们在等待2秒时重用工作者线程做些其他 事,这对提高应用程序的可伸缩性非常重要:借助于异步函数我们拥有了线性的程序控制 流,但它的执行依然是异步的。这虽然好用,但是难以理解.如果程序中有两个连续的await操作符,此时程序如何工作有 —个常见的误解。很多人认为如果在另一个异步操作之后使用await函数,它们将会 并行运行。然而,事实上它们是顺序运行的,即第一个完成后第二个才会开始运行

六:注意事项:

a. 关联async和await有一定的限制°例如,在C# 5.0中,不能把控制台程序的Main方法 标记为async。不能在catch、finally. lock或unsafe代码块中使用await操作符。不允许对任 何异步函数使用ref或out参数。还有其他微妙的地方,但是以上已经包括了主要的需要注意 的地方。C#6.0去除了其中一些限制,由于编译器内部进行了改进,可以在catch和finally 代码块中使用await关键字.

b. 尽管 很多程序员几乎开始为每个方法使用async修饰符,我还是想强调如果方法本来无需异步 或并行运行,那么将该方法标注为async是没有道理的。调用async方法会有显著的性能 损失,通常的方法调用比使用async关键字的同样的方法调用要快上40〜50倍°请注意 这一点

 

1. 使用await操作符获取异步任务结果

class Program
    {
        static void Main(string[] args)
        {
            Task t = AsynchronyWithTPL();
            t.Wait();

            t = AsynchronyWithAwait();
            t.Wait();
            Read();
        }
        //当程序运行时运行了两个异步操作。其中一个是标准的TPL模式的代码,第二个使用了 C#的新特性async和awaiL AsynchronyWithTPL方法启动了一个任务,
        //运行两秒后返回关于工作者线程信息的字符串。然后我们定义了一个后续操作,用于在异步操作完成后打印出 该操作结果,
        //还有另一个后续操作,用于万一有错误发生时打印出异常的细节。最终,返回 了一个代表其中一个后续操作任务的任务,并等待其在Main函数中完成
        static Task AsynchronyWithTPL()
        {
            Task<string> t = GetInfoAsync("Task 1");
            Task t2 = t.ContinueWith(task => WriteLine(t.Result),
                TaskContinuationOptions.NotOnFaulted);
            Task t3 = t.ContinueWith(task => WriteLine(t.Exception.InnerException),
                TaskContinuationOptions.OnlyOnFaulted);

            return Task.WhenAny(t2, t3);
        }
        //在AsynchronyWithAwait方法中,我们对任务使用await并得到了相同的结果。这和编 写通常的同步代码的风格一样,即我们获取任务的结果,打印出结果,
        //如果任务完成时带有 错误则捕获异常。关键不同的是这实际上是一个异步程序。使用await后,C#立即创建了一 个任务,其有一个后续操作任务,
        //包含了 await操作符后面的所有剩余代码。这个新任务也 处理了异常传播。然后,将该任务返回到主方法中并等待其完成

        static async Task AsynchronyWithAwait()
        {
            try
            {
                string result = await GetInfoAsync("Task 2");
                WriteLine(result);
            }
            catch (Exception ex)
            {
                WriteLine(ex);
            }
        }

        static async Task<string> GetInfoAsync(string name)
        {
            await Task.Delay(TimeSpan.FromSeconds(2));
            //throw new Exception("Boom!");
            return
                $"Task {name} is running on a thread id {CurrentThread.ManagedThreadId}." + 
                $" Is thread pool thread: {CurrentThread.IsThreadPoolThread}";
        }
    }
使用await获得结果

2. 在lambda表达式中使用await操作符

class Program
    {
        static void Main(string[] args)
        {
            Task t = AsynchronousProcessing();
            t.Wait();
        }
        //首先,由于不能在Main方法中使用async,我们将异步函数移到了 Asynchronous- Processing 方法中°
        //然后使用async关键字声明了一个lambda表达式:由于任何lambda 表达式的类型都不能通过lambda自身来推断,
        //所以不得不显式向C#编译器指定它 的类型。在本例中,该类型说明该lambda表达式接受一个字符串参数,并返回一个 Task<string> 对象

        //我们定义了 lambda表达式体。有个问题是该方法被定义为返回一个Task<string> 对象,但实际上返回的是字符串,
        //却没有编译错误!这是因为C#编译器自动产生一个任务 并返回给我们。最后一步是等待异步lambda表达式执行并打印岀结果.
        static async Task AsynchronousProcessing()
        {
            Func<string, Task<string>> asyncLambda = async name => {
                await Task.Delay(TimeSpan.FromSeconds(2));
                return
                    $"Task {name} is running on a thread id {CurrentThread.ManagedThreadId}." +
                    $" Is thread pool thread: {CurrentThread.IsThreadPoolThread}";
            };

            string result = await asyncLambda("async lambda");

            WriteLine(result);
        }
    }
lambda表达式中使用await操作符

3. 对连续的异步任务使用await操作符(当代码中有多个连续的await方法时程序的实际流程是怎样的)

class Program
    {
        static void Main(string[] args)
        {
            Task t = AsynchronyWithTPL();
            t.Wait();

            t = AsynchronyWithAwait();
            t.Wait();
        }
        /*AsynchronyWithTPL方法模仿了 AsynchronyWithAwait的程序流。我们需要一个容器任 务来处理所有相互依赖的任务。然后启动主任务,给其加了一组后续操作。
         * 当该任务完成 后,会打印出其结果。然后又启动了一个任务,在该任务完成后会依次运行更多的后续操 作。为了测试对异常的处理,
         * 当运行第二个任务时故意抛出一个异常,并打印出异常信息。 这组后续操作创建了与第一个方法中一样的程序流。如果用它与await方法比较,
         * 可以看到 它更容易阅读和理解。唯一的技巧是请记住异步并不总是意味着并行执行
         */
        static Task AsynchronyWithTPL()
        {
            var containerTask = new Task(() => { 
                Task<string> t = GetInfoAsync("TPL 1");
                t.ContinueWith(task => {
                    WriteLine(t.Result);
                    Task<string> t2 = GetInfoAsync("TPL 2");
                    t2.ContinueWith(innerTask => WriteLine(innerTask.Result),
                        TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.AttachedToParent);
                    t2.ContinueWith(innerTask => WriteLine(innerTask.Exception.InnerException),
                        TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.AttachedToParent);
                    },
                    TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.AttachedToParent);

                t.ContinueWith(task => WriteLine(t.Exception.InnerException),
                    TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.AttachedToParent);
            });

            containerTask.Start();
            return containerTask;
        }
        /*使用了两个await声明。最 重要的一点是该代码依然是顺序执行的,Async 2任务只有等之前的任务完成后才会开始执 行。
         * 当阅读该代码时,程序流很清晰,可以看到什么先运行,什么后运行。但该程序如何 是异步程序呢?首先,它不总是异步的。
         * 当使用await时如果一个任务已经完成,我们会异步地得到该任务结果。否则,当在代码中看到await声明时,
         * 通常的行为是方法执行到该await代码行时将立即返回,并且剩下的代码将会在一个后续操作任务中运行。
         * 因此等 待操作结果时并没有阻塞程序执行,这是一个异步调用。当AsynchronyWithAwait方法中 的代码在执行时,
         * 除了在Main方法中调用t.Wait外,我们可以执行任何其他任务。然而, 主线程必须等待直到所有异步操作完成,
         * 否则主线程完成后所有运行异步操作的后台线程 会停止运行
         */
        static async Task AsynchronyWithAwait()
        {
            try
            {
                string result = await GetInfoAsync("Async 1");
                WriteLine(result);
                result = await GetInfoAsync("Async 2");
                WriteLine(result);
            }
            catch (Exception ex)
            {
                WriteLine(ex);
            }
        }

        static async Task<string> GetInfoAsync(string name)
        {
            WriteLine($"Task {name} started!");
            await Task.Delay(TimeSpan.FromSeconds(2));
            if(name == "TPL 2")
                throw new Exception("Boom!");
            return
                $"Task {name} is running on a thread id {CurrentThread.ManagedThreadId}." +
                $" Is thread pool thread: {CurrentThread.IsThreadPoolThread}";
        }
    }
连续await

 

标签:异步,Task,C#,await,---,任务,WriteLine,async
From: https://www.cnblogs.com/apple-hu/p/18470474

相关文章

  • OpenCV 简介与在 Python 和 C# 中的使用
    一、OpenCV概述(一)什么是OpenCVOpenCV(OpenSourceComputerVisionLibrary)是一个开源的计算机视觉库,它提供了丰富的函数和工具,用于处理图像和视频数据。最初由英特尔公司开发,现在由一个开源社区维护和扩展。OpenCV支持多种操作系统,包括Windows、Linux和MacOS等,并且可......
  • Canvas 在前端中的高级应用
    一、引言在前端开发领域,HTML5的 <canvas> 元素为网页带来了强大的绘图和动画功能。它不仅可以用于绘制简单的图形,还能够实现复杂的交互效果和动画场景。以下将详细介绍 canvas 的使用方法,并展示一些高级案例。二、Canvas基础(一)创建Canvas元素在HTML页面中,可以通......
  • 《RabbitMQ系列》之RabbitMQ的4种Exchange
    大家好,我是tc,今天为大家介绍一下RabbitMQ中的4种exchange,水平一般,能力有限,若有错误之处,欢迎指正。 对RabbitMQ稍有了解的朋友应该都知道,在RabbitMQ中,一个有4中Exchange,分别是direct、topic、fanout、headers。其实,还有一个默认的交换机,称为defaultexchange,其本质也是一个di......
  • keycloak~token有效期与session有效期
    一refresh_token刷新access_tokenKeycloak会话管理中,获取到accessToken和refreshToken后,基于accessToken交换用户数据或者参与KeycloakAPI的请求,当accessToken过期的时候,可使用refreshToken去交换新的accessToken和refreshToken。这块根据之前的refresh_token就得到了一个新的......
  • java_day12_Collection、List
    CollectionCollection【接口】:我们通过帮助文档发现,Collection是一个接口,不能直接new对象根据元素是否可以发生重复,继续分类-List【接口】元素可以发生重复,且有索引的概念ArrayList-Set【接口】元素不可以发生重复,没有索引借助ArrayList子类对......
  • CATIA软件许可管理最佳实践
    在当今的工程设计领域,CATIA软件已成为众多企业不可或缺的工具。然而,随着软件使用的广泛普及,许可管理变得尤为关键。本文将为您探讨CATIA软件许可管理的最佳实践,助您在确保合规性的同时,实现成本效益的最大化。一、明确许可需求与策略首先,企业需要明确自身的CATIA软件许可需求。这......
  • R语言报错:Error in as.double(y) : cannot coerce type 'S4' to vector of type 'd
    在RStudio中使用plot函数报错:查询解决方案是缺少Rgraphviz包,执行以下代码:source("http://bioconductor.org/biocLite.R")biocLite(c("graph","Rgraphviz")) 又提示 于是添加 plot使用成功 ......
  • JVM第7篇-性能监控 & JVM性能调优案例
    JVM第7篇-性能监控&JVM性能调优案例性能监控一、JVM监控及诊断工具-命令行篇1.1基础故障处理工具1.1.1jps:虚拟机进程状况工具命令格式使用1.1.2jstat:虚拟机统计信息监视工具命令格式使用1.1.3jinfo:Java配置信息工具命令格式使用1.1.4jmap:Java内存映像分......
  • 算法-中缀转后缀表达式(C++)
    因为操作数在后缀表达式中它们的顺序与中缀表达式一致,所以操作数不需要进行特殊处理,所以遇到数字就输出,遇到符号就经过处理再输出所以需要用一个存储结构存符号为什么用栈存储:要利用后进先出的特性出栈也就是加入到后缀表达式中,一部分一部分处理,处理完一部分,要处理他邻近的......
  • LLAMAFACTORY:一键优化大型语言模型微调的利器
    人工智能咨询培训老师叶梓转载标明出处模型适配到特定的下游任务,通常需要进行微调(fine-tuning),这一过程往往需要大量的计算资源。为了解决这一问题,来自北京航空航天大学和北京大学的研究人员共同开发了LLAMAFACTORY,这是一个统一的框架,集成了多种前沿的高效训练方法,使得用户可......