异步编程中的最佳做法,上下文问题

作者: 网络编程  发布:2019-09-21

async / await 使异步代码更便于写,因为它遮掩了累累细节。 比比较多那些细节都捕获在 SynchronizationContext 中,那个或者会改换异步代码的行为完全出于您试行你的代码的条件(举例WPF,Winforms,调整台或ASP.NET)所调整。 若果尝试通过忽略 SynchronizationContext 发生的影响,您大概遭受死锁和竞争条件意况。

原稿链接

SynchronizationContext 调节义务再而三的调节措施和岗位,况且有无尽两样的上下文可用。 借使您正在编辑一个 WPF 应用程序,创设二个网址或利用 ASP.NET 的API,你应该清楚您早就使用了叁个非常的 SynchronizationContext 。

方今来,涌现了众多有关 Microsoft .NET Framework 4.5中新扩展了对 async 和 await 扶助的新闻。 本文意在作为读书异步编制程序的“第二步”;小编假诺您已阅读过关于这一面包车型客车至少一篇介绍性小说。 本文不提供其余新内容,Stack Overflow、MSDN 论坛和 async/await FAQ 那类在线财富提供了一样的提出。 本文只重视介绍部分溺水在文书档案海洋中的最好做法。

 

本文中的最棒做法越来越大程度上是“指引规范”,并非实在法规。 个中每一种指引标准都有一对例外情形。 作者将分解各样指点标准背后的开始和结果,以便能够精晓地问询曾几何时适用以及几时不适用。 图 1 中计算了这一个教导标准;小编将要以下各节中逐条研究。

SynchronizationContext in a console application

让我们来拜望调整台应用程序中的一些代码:

public class ConsoleApplication
{
    public static void Main()
    {
        Console.WriteLine($"{DateTime.Now.ToString("T")} - Starting");
        var t1 = ExecuteAsync(() => Library.BlockingOperation());
        var t2 = ExecuteAsync(() => Library.BlockingOperation()));
        var t3 = ExecuteAsync(() => Library.BlockingOperation()));

        Task.WaitAll(t1, t2, t3);
        Console.WriteLine($"{DateTime.Now.ToString("T")} - Finished");
        Console.ReadKey();
    }

    private static async Task ExecuteAsync(Action action)
    {
        // Execute the continuation asynchronously
        await Task.Yield();  // The current thread returns immediately to the caller
                             // of this method and the rest of the code in this method
                             // will be executed asynchronously

        action();

        Console.WriteLine($"{DateTime.Now.ToString("T")} - Completed task on thread {Thread.CurrentThread.ManagedThreadId}");
    }
}

当中 Library.BlockingOperation() 是三个第三方库,我们用它来阻塞正在利用的线程。 它能够是其余阻塞操作,但是为了测量检验的指标,您能够接纳 Thread.Sleep(2) 来代替完结。

运维程序,输出结果为:

16:39:15 - Starting

16:39:17 - Completed task ``on thread 11

16:39:17 - Completed task ``on金沙澳门官网thread 10

16:39:17 - Completed task ``on thread 9

16:39:17 - Finished

在示范中,大家制造八个任务阻塞线程一段时间。 Task.Yield 强制叁个方法是异步的,通过调整那一个讲话之后的保有剧情(称为_continuation_)来执行,但当下将调节权再次回到给调用者(Task.Yield 是报告调治者"小编已管理到位,可以将奉行权让给其余的线程",至于最后调用哪个线程,由调整者决定,大概下五个调节的线程依然自个儿笔者)。 从出口中能够看到,由于 Task.Yield 全体的操作最后并行实施,总试行时间独有两秒。

 

图 1 异步编制程序指引原则计算

SynchronizationContext in an ASP.NET application

设若大家想在 ASP.NET 应用程序中援引那一个代码,我们将代码 Console.WriteLine 转变为 HttpConext.Response.Write 就能够,大家能够见见页面上的输出:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        HttpContext.Response.Write($"{DateTime.Now.ToString("T")} - Starting");
        var t1 = ExecuteAsync(() => Library.BlockingOperation()));
        var t2 = ExecuteAsync(() => Library.BlockingOperation()));
        var t3 = ExecuteAsync(() => Library.BlockingOperation()));

        Task.WaitAll(t1, t2, t3);
        HttpContext.Response.Write($"{DateTime.Now.ToString("T")} - Finished");

        return View();
    }

    private async Task ExecuteAsync(Action action)
    {
        await Task.Yield();

        action();
        HttpContext.Response.Write($"{DateTime.Now.ToString("T")} - Completed task on thread {Thread.CurrentThread.ManagedThreadId}");
    }
}

作者们会意识,在浏览器中运营此页面后不会加载。 看来我们是引进了二个死锁。那么这里终归发生了什么样呢?

死锁的由来是调控台应用程序调治异步操作与 ASP.NET 分裂。 即使调控台应用程序只是调节线程池上的职分,而 ASP.NET 确定保障同一 HTTP 伏乞的保有异步职务都按顺序推行。 由于 Task.Yield 将剩余的办事排队,并登时将调节权重回给调用者,因而我们在运维Task.WaitAll 的时候有八个等待操作。 Task.WaitAll 是三个围堵操作,类似的短路操作还会有如 Task.Wait 或 Task.Result,因而阻止当前线程。

ASP.NET 是在线程池上调节它的义务,阻塞线程而不是引致死锁的从头到尾的经过。 可是由于是逐个实施,那致使不容许等待操作起来施行。 倘诺她们不只怕起动,他们将恒久不能够成功,被截留的线程不能够继续。

此调解机制由 SynchronizationContext 类调整。 每当大家拭目以俟职务时,在等候的操作完毕后,在 await 语句(即持续)之后运行的享有内容将在如今 SynchronizationContext 上被调节。 上下文决定了如何、曾几何时和在何地实践职责。 您能够采纳静态 SynchronizationContext.Current 属性采访当前上下文,并且该属性的值在 await 语句此前和后来平昔相同。

在调整台应用程序中,SynchronizationContext.Current 始终为空,那意味连接能够由线程池中的任何空闲线程拾取,那是在率先个示范中能并行实施操作的因由。 可是在大家的 ASP.NET 调节器中有一个AspNetSynchronizationContext,它确认保证前边提到的各样管理。

要点一:

实际不是选用阻塞任务同步方法,如 Task.Result,Task.Wait,Task.WaitAll 或 Task.WaitAny。 调整台应用程序的 Main 方法前段时间是该法则独一的两样(因为当它们获取完全异步时的表现会有着改观)。

 

“名称” 说明 异常
避免 Async Void 最好使用 async Task 方法而不是 async void 方法 事件处理程序
始终使用 Async 不要混合阻塞式代码和异步代码 控制台 main 方法
配置上下文 尽可能使用 ConfigureAwait(false) 需要上下文的方法

本文由金沙澳门官网发布于网络编程,转载请注明出处:异步编程中的最佳做法,上下文问题

关键词: 金沙澳门官网

上一篇:Git开垦必知必会
下一篇:没有了