当前位置: 首页 > news >正文

做企业网站排名优化要多少钱wordpress评论不了

做企业网站排名优化要多少钱,wordpress评论不了,网络公司经营范围包括哪些,科技部网站改版方案异步编程长时间以来一直都是那些技能高超、喜欢挑战自我的开发人员涉足的领域 — 这些人愿意花费时间#xff0c;充满热情并拥有心理承受能力#xff0c;能够在非线性的控制流程中不断地琢磨回调#xff0c;之后再回调。 随着 Microsoft .NET Framework 4.5 的推出#xff… 异步编程长时间以来一直都是那些技能高超、喜欢挑战自我的开发人员涉足的领域 — 这些人愿意花费时间充满热情并拥有心理承受能力能够在非线性的控制流程中不断地琢磨回调之后再回调。 随着 Microsoft .NET Framework 4.5 的推出C# 和 Visual Basic 让我们其他人也能处理异步工作普通程序员也可以像编写同步方法一样轻松编写异步方法。 不再使用回调。 不再需要将代码从一个同步环境显式封送到另一个同步环境。 不再需要担心结果或异常的流动。 不再需要想方设法地改造现有语言功能而简化异步开发。 简言之没有麻烦了。 当然现在很容易开始编写异步方法 但是想要真正做好仍然需要了解后台的工作原理。 无论何时当某种语言或框架提高了开发人员可以编程的抽象级别时也总是隐含了不可见的性能成本。 在许多情况下这些成本是微不足道要实现大量方案的广大开发人员可以也应该忽略它们。 然而对于更高级的开发人员来说还是有必要去真正了解都有哪些成本如果这些成本最终成为阻碍我们能够采取必要的步骤予以避免。 对于 C# 和 Visual Basic 中的异步方法功能就存在这种情况。 在本文中我将探讨异步方法的各种细节使您能够全面了解异步方法的内在实现方式并讨论其中涉及的其他一些略有区别的成本。 请注意我要传达的信息并不是要鼓励您以追求微优化和性能的名义将可读代码改造成不可维护的代码。 它只是为提供您一些信息帮助您诊断可能遇到的问题并提供一套帮助您解决此类潜在问题的工具。 另请注意本文以 .NET Framework 4.5 为基础具体实施细节可能在最终版本发布之前有所变动。 获得正确的思维模式 几十年来开发人员一直使用 C#、Visual Basic、F# 和 C 等高级语言开发高效的应用程序。 这方面的经验使得开发人员了解了各种操作的相关成本这种了解也推动了最佳开发实践。 例如对于大多数用例调用同步方法相对比较便宜而当编译器能够将被调用方内嵌到调用点时则更加便宜。 因此开发人员学会了将代码重构为小的、可维护的方法一般无需考虑由于方法调用数量增加而带来的负面影响。 这些开发人员对调用方法的意义有着特定的思维模式。 在引入异步方法后他们需要有新的思维模式。 C# 和 Visual Basic 语言以及编译器会让人产生异步方法就是其同步方法的对应版本的错觉但实际情况并不是如此。 编译器最终会代替开发人员生成大量代码这些代码与过去实现异步时必须由开发人员手动编写并维护的样板代码的数量类似。 此外编译器生成的代码会在 .NET Framework 中调用库代码再次代替开发人员完成更多的工作。 要获得正确的思维模式并使用这一模式做出合适的开发决策重要的一点是了解编译器代替您生成了哪些内容。 积极思考不要多话 使用同步代码时附带空白主体的方法几乎是免费的。 但对异步方法来说情况并非如此。 以下面的异步方法为例其主体包含一个语句并且由于缺乏等待而最终同步运行 public static async Task SimpleBodyAsync() {Console.WriteLine(Hello, Async World!); } 中间语言 (IL) 反编译器在编译完成后将揭示此函数的本质输出类似于图 1 所示内容。 简单的单行式命令已扩展到两个方法并且其中一个存在于帮助程序状态机类中。 首先有一个包含了基本签名的存根方法其签名与开发人员编写的基本签名相同该方法具有相同的名称具有相同的可视性接受相同的参数并保留了返回类型但是此存根不包含开发人员编写的任何代码。 相反它包含了设置样板。 设置代码对用来代表异步方法的状态机进行初始化然后调用状态机上的辅助 MoveNext 方法来启动状态机。 此状态机类型将保留异步方法的状态如有必要允许在异步等待点之间保持该状态。 它也包含用户编写的方法的主体但已经过改造从而允许结果和异常提升到返回的 Task并维持方法的当前位置以便完成等待后在此处恢复执行等等。 图 1 异步方法样板 [DebuggerStepThrough]     public static Task SimpleBodyAsync() {SimpleBodyAsyncd__0 d__ new SimpleBodyAsyncd__0();d__.t__builder AsyncTaskMethodBuilder.Create();d__.MoveNext();return d__.t__builder.Task; }[CompilerGenerated] [StructLayout(LayoutKind.Sequential)]private struct SimpleBodyAsyncd__0 : t__IStateMachine {private int 1__state;public AsyncTaskMethodBuilder t__builder;public Action t__MoveNextDelegate;public void MoveNext() {try {if (this.1__state -1) return;Console.WriteLine(Hello, Async World!);}catch (Exception e) {this.1__state -1;this.t__builder.SetException(e);return;}this.1__state -1;this.t__builder.SetResult();}... } 当考虑要调用的异步方法的成本时请牢记这一样板。 MoveNext 方法中的 try/catch 块可能会阻止样板被实时 (JIT) 编译器内嵌所以至少我们现在拥有了方法调用成本而在同步情况下则可能不会有如此小的方法主体。 我们有多个对 Framework 例程如 SetResult的调用 并且我们在状态机类型上有多个写入字段。 当然我们需要针对 Console.WriteLine 的成本权衡这一切因为它可能会主宰所有其他涉及到的成本它需要锁定它执行 I/O 等。 此外请注意基础结构为您做出的优化。 例如状态机类型就是一个结构。 如果此方法因为正在等待尚未完成的实例在这简单的方法中实例永远不会完成而需要暂停其执行该结构只会被封装到堆。 因此异步方法的样板将不会产生任何分配。 编译器和运行时共同努力以尽量减少基础结构中涉及的分配数量。 了解何时不使用异步 .NET Framework 应用多种优化尝试为异步方法生成高效的异步实现。 但是开发人员通常具备领域知识然后产生一些优化如果考虑所追求的通用性这些优化如果由编译器和运行来自动应用是有风险和不明智的。 明确这一点后实际上会有利于开发人员避免在一小部分用例中使用异步方法特别是对于将以一种更精准的方式访问的库方法。 一般来说如果已知方法实际上可以同步完成因为方法依赖的数据是已经可用的尤其如此。 设计异步方法时Framework 开发人员往往花费大量的时间优化对象分配。 这是因为分配是异步方法基础结构中可能出现的最大性能成本之一。 分配一个对象的行为通常相当便宜。 分配对象类似于向购物车中放入商品此时将商品放入车内不需要花费太多的精力而当您实际结账时则需要拿出钱包并投入大量的资源。 虽然分配通常开销很低但谈到应用程序的性能时产生的垃圾收集实在令人不爽。 垃圾收集行为包括扫描当前已分配的部分对象和查找不再被引用的对象。 分配的对象越多执行这种标记的时间就越长。 此外分配的对象越大并且分配的数量越多垃圾收集发生的频率就越大。 通过这种方式分配会对系统产生全局影响异步方法生成的垃圾越多整个程序的运行速度就越慢即使异步方法自身的微基准不显示明显的成本。 对于实际产生执行的异步方法由于等待尚未完成的对象异步方法基础结构需要分配一个 Task 对象以从方法返回而此 Task 对象将用作这一特殊调用的唯一引用。 然而许多异步方法调用无需产生即可完成。在这种情况下异步方法基础结构可能返回一个已缓存并完成的 Task而该 Task 可以反复使用以避免分配不必要的 Task。 但是能这么做的情况不多例如当异步方法是一个非泛型 Task、TaskBoolean 或 TaskTResult其中 TResult 是引用类型时异步方法的结果为空。 虽然这一组合在未来可能会扩大但如果您具备正在实施的操作的领域知识您可以做的更好。 考虑实现类似 MemoryStream 的类型。 MemoryStream 由 Stream 派生而来因此可以覆盖 Stream 的新的 .NET 4.5 ReadAsync、WriteAsync 和 FlushAsync 方法从而优化 MemoryStream 的实现。 由于读取操作与内存中的缓冲区刚好背道而驰因此如果 ReadAsync 同步运行仅仅复制内存就可获得更好的性能。使用异步方法实现这一操作应该类似于如下所示 public override async Taskint ReadAsync(byte [] buffer, int offset, int count,CancellationToken cancellationToken) {cancellationToken.ThrowIfCancellationRequested();return this.Read(buffer, offset, count); } 非常简单。 由于 Read 是一个同步调用并且此方法中没有会产生控制的等待因此 ReadAsync 的所有调用实际上会同步完成。 现在让我们考虑流的一个标准使用模式例如复制操作 byte [] buffer new byte[0x1000];int numRead;while((numRead await source.ReadAsync(buffer, 0, buffer.Length)) 0){await source.WriteAsync(buffer, 0, numRead); } 这里要注意的是源流上针对这一特定调用系列的 ReadAsync 总是与同一计数参数缓冲区的长度同时调用因此极有可能返回值读取的字节数也将重复。 除了某些极少数的情况外ReadAsync 的异步方法实现不可能使用缓存的 Task 获得返回值但是您可以。 请考虑如图 2 所示重写此方法。 利用此方法的一些特点及其常见的使用方案我们现在能够用不寻常的方式优化分配而不期待底层基础结构实现此优化。 因此每次调用 ReadAsync 都会检索与之前调用时数量相同的字节我们可以通过返回与之前调用返回的相同的 Task 来彻底避免 ReadAsync 方法产生的调用开销。 而对于像这样的低级别操作我们期望能够快速并重复调用这样的优化可以产生显著的变化特别是在垃圾收集数量方面。 图 2 优化任务分配 private Taskint m_lastTask;public override Taskint ReadAsync(byte [] buffer, int offset, int count,CancellationToken cancellationToken) {if (cancellationToken.IsCancellationRequested) {var tcs new TaskCompletionSourceint();tcs.SetCanceled();return tcs.Task;}try {int numRead this.Read(buffer, offset, count);return m_lastTask ! null numRead m_lastTask.Result ? m_lastTask : (m_lastTask Task.FromResult(numRead));}catch(Exception e) {var tcs new TaskCompletionSourceint();tcs.SetException(e);return tcs.Task;} } 当方案中规定了缓存时可能会执行相关的优化以避免任务分配。 考虑一种旨在下载特定网页内容并缓存其成功下载的内容以备将来访问的方法。 这种功能可能使用异步方法编写如下所示使用 .NET 4.5 中新的 System.Net.Http.dll 库 private static ConcurrentDictionarystring,string s_urlToContents;public static async Taskstring GetContentsAsync(string url) {string contents;if (!s_urlToContents.TryGetValue(url, out contents)){var response await new HttpClient().GetAsync(url);contents response.EnsureSuccessStatusCode().Content.ReadAsString();s_urlToContents.TryAdd(url, contents);}return contents; } 这是一个简单的实现。 而对于无法从缓存得到满足的 GetContentsAsync 调用为表示此次下载而构建的新 Taskstring 的开销与网络成本相比是可以忽略不计的。 但是如果内容可以从缓存得到满足它可能就是不可忽略的成本对象分配只是包装并退回已经可用的数据。 要避免此成本为了满足性能目标而这样做您可以如图 3 所示重写此方法。 我们现在有两种方法同步公共方法和公共方法委托的异步私有方法。 字典现在缓存生成的任务而不是自身的内容所以要未来尝试下载已经成功下载的页面可以通过访问字典以返回已存在的任务而得到实现。 在内部我们也利用 Task 上的 ContinueWith 方法它允许我们在任务完成后将其储存到字典中 — 但仅限下载成功的情况下。 当然此代码较为复杂并需要花费更多的心思编写和维护而所有的性能优化都一样除非性能测试证明即使复杂却会产生重大且必要的影响否则无需花费时间编写这样的代码。 这种优化是否发挥作用实际上要取决于使用方案。 您会希望引入一组代表常见使用模式的测试并分析这些测试以判断这种复杂能否以一种有意义的方式提高代码的性能。 图 3 手动缓存任务 private static ConcurrentDictionarystring,Taskstring s_urlToContents;public static Taskstring GetContentsAsync(string url) {Taskstring contents;if (!s_urlToContents.TryGetValue(url, out contents)) {contents GetContentsAsync(url);contents.ContinueWith(delegate {s_urlToContents.TryAdd(url, contents);}, CancellationToken.None,TaskContinuationOptions.OnlyOnRanToCompletion |TaskContinuatOptions.ExecuteSynchronously,TaskScheduler.Default);}return contents; }private static async Taskstring GetContentsAsync(string url) {var response await new HttpClient().GetAsync(url);return response.EnsureSuccessStatusCode().Content.ReadAsString(); } 另一个需要考虑的与任务相关的优化是您是否需要从异步方法返回的任务。 C# 和 Visual Basic 都支持创建返回 void 的异步方法在这种情况下永远都不需要为方法分配任务。 您始终应该编写从库中公开的异步方法以返回 Task 或 TaskTResult因为作为库的开发人员您不知道使用者是否愿意等待方法完成。 但是对于某些内部使用方案返回 void 的异步方法仍然占有一席之地。 返回 void 的异步方法存在的主要原因是支持现有的事件驱动环境如 ASP.NET 和 Windows Presentation Foundation (WPF)。 它们通过使用 async 和 await使得实施按钮处理程序、页面加载事件等变得很容易。 如果您确实考虑使用异步 void 方法请注意异常的处理转义异步 void 方法的异常会在异步 void 方法被调用时冒出并进入 SynchronizationContext 当时的状态。 注意环境 .NET Framework 中有很多类型的“环境”LogicalCallContext、SynchronizationContext、HostExecutionContext、SecurityContext、ExecutionContext 等单纯从数量上看您可能会认为 Framework 的开发人员是受到金钱的激励而推出这么多新环境但是我可以向您保证我们不是。 这些环境中的一部分与异步方法关系非常密切不仅仅是在功能上它们对异步方法的性能也有很大的影响。   SynchronizationContext SynchronizationContext 在异步方法中扮演着重要角色。 “同步环境”是对封送能力的抽象即以给定库或框架规定的方式封送委托调用的能力。 例如WPF 提供一个 DispatcherSynchronizationContext 用来表示调度程序的 UI 线程向此同步环境发布委托会导致该委托排队等待被其线程上的调度程序执行。 ASP.NET 提供一个 AspNetSynchronizationContext用于确保在处理 ASP.NET 请求过程中出现的异步操作按顺序执行并与正确的 HttpContext 状态相关联。 其他在此就不一一列举了。 总之在 NET Framework 中 SynchronizationContext 约有 10 种具体的实现一些是公共实现而一些则是内部实现。 当等待 .NET Framework 提供的任务和其他可等待类型时这些类型的“等待程序”如 TaskAwaiter在等待发出时将捕获当前的 SynchronizationContext。 可等待类型完成时如果已经捕获一个当前的 SynchronizationContext则代表异步方法其余部分的延续将发布到 SynchronizationContext。 这样正在编写从 UI 线程调用的异步方法的开发人员不需要为了修改 UI 控件手动将调用封送回 UI 线程这样的封送由 Framework 基础结构自动处理。 遗憾的是这样的封送也涉及到成本。 对于使用 await 实现其控制流的应用程序开发人员而言这种自动封送通常都是正确的解决方案。 但是库往往不太一样。 应用程序开发人员通常需要这样的封送是因为他们的代码会关注自身正在运行的环境如能够访问 UI 控件或能够访问 HttpContext 以获得正确的 ASP.NET 请求。 但是大多数的库并不受此约束。 因此这种自动封送经常是完全不必要的成本。 再次以前文所示的将数据从一个流复制到另一个流的代码为例 byte [] buffer new byte[0x1000];int numRead;while((numRead await source.ReadAsync(buffer, 0, buffer.Length)) 0) {await source.WriteAsync(buffer, 0, numRead); } 如果此复制操作是从 UI 线程调用那么每一个等待的读、写操作都将强制完成回 UI 线程。 对于 1 MB 的源数据和异步完成读、写的流大多数的流都是这样这意味着从后台到 UI 线程有多达 500 个跃点。 为解决这一问题Task 和 TaskTResult 类型提供了 ConfigureAwait 方法。 ConfigureAwait 接受一个控制此封送行为的 Boolean continueOnCapturedContext 参数。 如果使用默认的 true等待将在捕获的 SynchronizationContext 上自动完成。 但是如果使用 falseSynchronizationContext 将被忽略并且 Framework 将尝试在之前异步操作完成的位置继续执行。 将此操作合并到流复制代码会产生下列更高效的版本 byte [] buffer new byte[0x1000];int numRead;while((numRead await  source.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false)) 0) {await source.WriteAsync(buffer, 0, numRead).ConfigureAwait(false); } 对库开发人员来说这种性能影响自身已足够保证一直使用 ConfigureAwait除非在极少数情况下库对其环境具有领域知识并且不需要访问正确的环境以执行方法的主体。 除性能之外还有一个在库代码中使用 ConfigureAwait 的原因。 假设上述的代码没有 ConfigureAwait并处于一个从 WPF UI 线程调用的名为 CopyStreamToStreamAsync 的方法中如下所示 private void button1_Click(object sender, EventArgs args) {Stream src …, dst …;Task t CopyStreamToStreamAsync(src, dst);t.Wait(); // deadlock!} 在此开发人员应该已经写好 button1_Click 作为异步方法然后等待 Task而不是使用它的同步 Wait 方法。 Wait 方法有非常重要的用途但是将其用在像这样的 UI 线程中等待总是出错。 直到 Task 完成之后 Wait 方法才会返回。 如果是 CopyStreamToStreamAsync包含的等待尝试发布回到捕获的 SynchronizationContext并且当这些发布完成后方法才会完成因为发布会用于处理方法的其余部分。但是这些发布无法完成因为处理它们的 UI 线程在调用 Wait 时中断。 这是一个循环的依赖关系会导致死锁。 如果 CopyStreamToStreamAsync 改为使用 ConfigureAwait(false) 编写将不会产生循环依赖关系和死锁。 ExecutionContext ExecutionContext 是 .NET Framework 不可或缺的部分但是大多数开发人员都没有意识到它的存在。 ExecutionContext 是环境的鼻祖它封装了其他多个环境如 SecurityContext 和 LogicalCallContext并代表代码中应该自动跨异步点流动的一切。 无论您何时在 Framework 中使用 ThreadPool.QueueUserWorkItem、Task.Run、Delegate.BeginInvoke、Stream.BeginRead、WebClient.DownloadStringAsync 或其他异步操作如果可能实际上是捕获了 ExecutionContext通过 ExecutionContext.Capture然后捕获的环境将被用于处理提供的委托通过 ExecutionContext.Run。例如如果调用 ThreadPool.QueueUserWorkItem 的代码当时正在模拟 Windows 身份标识则将模拟相同的 Windows 身份标识来运行提供的 WaitCallback 委托。 如果调用 Task.Run 的代码首先将数据存储到 LogicalCallContext则相同的数据可通过提供的 Action 委托中的 LogicalCallContext 访问。ExecutionContext 也在任务的等待间流动。 Framework 中已有多个优化以避免在不必要时在捕获的 ExecutionContext 中捕获和运行因为这样做会非常昂贵。 但是像模拟 Windows 身份标识或将数据存储到 LogicalCallContext 等操作会阻碍这些优化。避免执行 ExecutionContext 的操作如 WindowsIdentity.Impersonate 和 CallContext.LogicalSetData将在使用异步方法以及使用一般异步功能时带来更好的性能。 走出垃圾收集的困扰 当涉及到局部变量时异步方法提供一个很好的假象。 在同步方法中C# 和 Visual Basic 中的局部变量都基于堆栈因此在存储这些局部变量时是无需堆分配的。 但在异步方法中当异步方法在等待点暂停时方法的堆栈将消失。 等待回复后方法要使用的数据则必须存储在某处。 因此C# 和 Visual Basic 编译器将局部变量“提升”到状态机结构中然后会在首次暂停的等待之后被封装到堆这样局部变量就可以在等待点之间继续存续。 在前文中我介绍了分配的对象数量如何影响垃圾收集的成本和频率同时分配的对象的大小也会影响垃圾收集的频率。 分配的对象越大垃圾收集运行的次数就越多。 因此在异步方法中需要提升到堆的局部变量越多垃圾收集发生的频率就越多。 在撰写此文时C# 和 Visual Basic 编译器有时会提升一些不必要的局部变量。 以下面的代码段为例 public static async Task FooAsync() {var dto DateTimeOffset.Now;var dt  dto.DateTime;await Task.Yield();Console.WriteLine(dt); } 在等待点之后根本就不读取 dto 变量因此在等待之前写入的值在通过等待后不需要保留。 但是编译器生成的用来存储局部变量的状态机类型仍然包含 dto 引用如图 4 所示。 图 4 局部变量提升 [StructLayout(LayoutKind.Sequential), CompilerGenerated]private struct FooAsyncd__0 : t__IStateMachine {private int 1__state;public AsyncTaskMethodBuilder t__builder;public Action t__MoveNextDelegate;public DateTimeOffset dto5__1;public DateTime dt5__2;private object t__stack;private object t__awaiter;public void MoveNext();[DebuggerHidden]public void t__SetMoveNextDelegate(Action param0); } 这稍微增大了真正必要的堆对象。 如果您发现垃圾收集发生的频率超过了预期请考虑您是否真的需要所有这些已经编码到异步方法的临时变量。 要避免状态机类出现过多字段请按以下示例重写 public static async Task FooAsync() {var dt DateTimeOffset.Now.DateTime;await Task.Yield();Console.WriteLine(dt); } 此外.NET 垃圾收集器 (GC) 是分代收集器也就是说它将对象组分成小组称为一代从更高层次来说新对象分配在第 0 代然后在收集期间存续的所有对象则提高一代.NET GC 目前使用第 0、1 和 2 代。这样一来GC 会从已知的对象空间的子集频繁收集从而能够加速收集过程。 它所依据的原理是新分配的对象很快也会消失而已经出现很长时间的对象则将继续出现很长时间。 这就是说如果一个对象在第 0 代存续它最后可能会出现一段时间但却因为这一额外时间而继续对系统施加压力。 这也意味着我们确实要确保不再需要的对象立即可以进行垃圾收集。 借助上文提及的提升局部变量将提升到在异步方法执行期间仍然保留在根级的类的字段中只要等待的对象能正确维护对委托的引用以在等待完成后立即调用。 在同步方法中JIT 编译器能够跟踪局部变量何时不能再访问并且此时可以帮助 GC 忽视这些作为根的变量从而使得不再被引用到其他任何地方的引用对象可以进行垃圾收集。 但是在异步方法中这些局部变量仍然可以引用这意味着如果它们真正成为局部变量则这些引用的对象就能存续更长时间。 如果您发现对象使用之后仍然保持有效请考虑在您使用以后归零引用这些对象的局部变量。 再次强调只有在您发现它确实导致性能问题时才执行这一操作否则将使代码不必要地复杂化。 此外C# 和 Visual Basic 编译器可以会在最终版本中做出更新或者未来可代替开发人员处理更多的此类方案所以今天编写的这类代码将来很可能会被淘汰。 避免复杂性 C# 和 Visual Basic 编译器在允许您使用 await 方面特别令人印象深刻几乎任何地方都可以使用。 Await 表达式可能用作更大表达式的一部分从而允许您等待可能有其他任何返回值的表达式中的 TaskTResult。例如以下代码将返回三个任务结果之和 public static async Taskint SumAsync(Taskint a, Taskint b, Taskint c) {return Sum(await a, await b, await c); }private static int Sum(int a, int b, int c) {return a b c; } C# 编译器允许您将“await b”表达式用作 Sum 函数的参数。 但是此处有多个等待结果以参数形式传递到 Sum并且由于计算顺序规则和异步在编译器中的实现方式这个特定的示例需要编译器“分散”前两个等待的临时结果。 正如您之前看到的那样局部变量通过提升到状态机类的字段中而在等待点之间保持不变。 但是在这个示例中值是 CLR 计算堆栈上的类这些值不会提升到状态机而是分散到单个的临时对象然后再被状态机引用。 当您在首个任务上完成等待并转而等待第二个时编译器会生成封送首个结果的代码并将封送的对象存储到状态机上的 t__stack 字段中。 当您在第二个任务上完成等待并转而等待第三个时编译器会生成从前两个值中创建 Tupleint,int 的代码并将此元祖存储到相同的 __stack 字段中。 这些都说明根据您编写的代码的不同最终可能会得到非常不同的分配模式。 请考虑改用以下方式编写 SumAsync public static async Taskint SumAsync(Taskint a, Taskint b, Taskint c) {int ra await a;int rb await b;int rc await c;return Sum(ra, rb, rc); } 这样改变之后编译器现在会向状态机类发出另外三个字段以存储 ra、rb 和 rc并且不会发生分散。 因此您不得不进行权衡选择分配较少的较大状态机还是选择分配较多的较小状态机。 在分散情况下分配的内存总量会更大因为每个分配的对象都会有自己的内存开销但是最终的性能测试可能会显示它会好得多。通常如前所述除非您已经发现分配是导致麻烦的原因否则您不应该考虑这些微优化操作但无论如何它有利于了解这些分配来自何处。 当然毋庸置疑之前的示例中有一个更大的成本您应该有所了解并积极思考。 直到三个等待都已完成之后代码才能够调用 Sum并且在等待之间不会进行任何工作。 产生的每一个等待都需要大量的工作因此需要处理的等待越少越好。 然后您应当立即使用 Task.WhenAll 等待所有任务而将所有这三个等待合并到一个 public static async Taskint SumAsync(Taskint a, Taskint b, Taskint c) {int [] results await Task.WhenAll(a, b, c);return Sum(results[0], results[1], results[2]); } Task.WhenAll 方法在此返回的 TaskTResult[] 在所有提供的任务完成之后才完成这样做的效率也远远高于单独等待每一个任务。 同时它还收集每个任务的结果并存储到数组。 如果您想要避免使用此数组可以强制绑定到适用于 Task 而不是 TaskTResult 的非泛型 WhenAll 方法。 对于最终性能您也可以采用混合方法首先检查是否所有的任务都已成功完成如果是请独立获得它们的结果 — 如果没有请等待没有完成的任务的 WhenAll。 这可以避免调用 WhenAll 时涉及不必要的分配例如分配需要传送到方法的参数数组。 并且如前所述我们也希望这个库函数能抑制环境封送。 图 5 中显示了此类解决方案。 图 5 应用多项优化 public static Taskint SumAsync(Taskint a, Taskint b, Taskint c) {return (a.Status TaskStatus.RanToCompletion b.Status TaskStatus.RanToCompletion c.Status TaskStatus.RanToCompletion) ? Task.FromResult(Sum(a.Result, b.Result, c.Result)) :SumAsyncInternal(a, b, c); }private static async Taskint SumAsyncInternal(Taskint a, Taskint b, Taskint c) {await Task.WhenAll((Task)a, b, c).ConfigureAwait(false);return Sum(a.Result, b.Result, c.Result); } 异步和性能 异步方法是一个功能强大的高效工具使您能够更轻松编写可伸缩和响应更快的库和应用程序。 请牢记一点异步不是对单个操作的性能优化。 采用同步操作并使其异步化必然会降低该操作的性能因为它仍然需要完成同步操作的所有工作只不过现在会有额外的限制和注意事项。 您关注异步的一个原因是其总体性能如果您采用异步方法编写所有内容整个系统的执行效果如何。这样您可以仅消耗执行需要的有价值的资源重叠 I/O 并实现更好的系统利用率。 .NET Framework 提供的异步方法实现已经进行了优化并且最终常常比使用现有模式和更多代码精心编写的异步实现能够提供同样优秀甚至更好的性能。 从现在开始无论您何时准备在 .NET Framework 中开发异步代码异步方法都是您的首选工具。 并且作为一个开发人员了解 Framework 代替您在这些异步方法中所作的一切对您非常有益这样可以确保获得尽可能好的最终结果。 Stephen Toub 是 Microsoft 并行计算平台团队的首席架构师。 衷心感谢以下技术专家感谢他们审阅了本文 Joe Hoag、Eric Lippert、Danny Shih 和 Mads Torgersen 原文链接https://msdn.microsoft.com/zh-cn/magazine/hh456402.aspx .NET社区新闻深度好文微信中搜索dotNET跨平台或扫描二维码关注
http://www.pierceye.com/news/262336/

相关文章:

  • 阿里万网怎么做网站向百度提交网站
  • 杭州模板网站好网站漂浮广告怎么做
  • a站为什么不火了站群宝塔批量建站
  • 免费网站建设pptlinkcat wordpress
  • html5 网站大连在哪里哪个省
  • php个人网站源码国家企业信用信息公示系统官网山东
  • 丹东做网站哪家好事件营销的方法
  • wordpress建立网站wordpress 媒体文件夹
  • 响应式网站 框架天津市建设行业联合会网站
  • mc做图的网站深圳注册公司推荐
  • 广告公司怎么设置网站关键字网片的重量计算公式
  • 外贸网站都有哪些wordpress侧边栏选项卡
  • 北京网站建设 降龙网asp.net建网站
  • 中小网站建设新手自己做网站
  • 建网站手机如何建设网站并与数据库相连
  • 网站建设公司的客户个人网站做哪些内容
  • 外贸公司网站推广怎么让公司建设网站
  • 网站开发语长沙网站建设商城
  • 手机刷网站排名软件建设银行网站怎么登录密码忘了怎么办
  • 利用海康威视做直播网站鞍山网站建设找金航
  • 做网站大概花多少钱商圈云分销软件下载
  • 万户网站制作网站跳转怎么做
  • 网站开发全程设计做网站推广的公司发展前景
  • 电商设计网站培训建设网站域名
  • 石家庄免费专业做网站网站建设投标书服务方案范本
  • 怀孕单子图片在线制作北京seo服务行者
  • 网站备案 子域名云南商城网站建设
  • 上传到服务器的网站打开是空白网站报备流程
  • 如何用ps做网站标识一个叫mit做app的网站
  • 网站免费网站免费主流网站模板