上篇首要深入分析了async,异步的世界【上】

【转】C#异步的世界【下】

 

 

接上篇:《C#异步的世界【上】

接上篇:《C#异步的世界【上】

上篇首要剖析了async\await在此之前的某个异步形式,前些天说异步的最主假如指C#5的async\await异步。在此为了便利的表明,大家称async\await从前的异步为“旧异步”,async\await为“新异步”。

上篇重要深入分析了async\await以前的局地异步格局,前几天说异步的机尽管指C#5的async\await异步。在此为了有利于的抒发,我们称async\await在此之前的异步为“旧异步”,async\await为“新异步”。

新异步的选择

只可以说新异步的行使太轻巧(要是仅仅只是说利用)

措施加上async修饰符,然后利用await关键字推行异步方法,就可以。对正是那般总结。像使用同步方法逻辑一样使用异步。

 public async Task<int> Test()
 {
     var num1 = await GetNumber(1);
     var num2 = await GetNumber(num1);
     var task =  GetNumber(num2);
     //或者
     var num3 = await task;
     return num1 + num2 + num3;
 }

新异步的行使

不得不说新异步的应用太轻松(要是仅仅只是说采用)

主意加上async修饰符,然后使用await关键字施行异步方法,就能够。对正是如此简约。像使用同步方法逻辑同样选用异步。

 public async Task<int> Test()
 {
     var num1 = await GetNumber(1);
     var num2 = await GetNumber(num1);
     var task =  GetNumber(num2);
     //或者
     var num3 = await task;
     return num1 + num2 + num3;
 }

新异步的优势

此前已经有了种种异步格局,为何还要引进和上学新的async\await异步呢?当然它断定是有其万分的优势。

笔者们分八个地方来分析:WinForm、WPF等单线程UI程序和Web后台服务程序。

新异步的优势

在此以前已经有了八种异步方式,为什么还要引进和上学新的async\await异步呢?当然它必然是有其特殊的优势。

作者们分五个方面来分析:WinForm、WPF等单线程UI程序和Web后台服务程序。

对此WinForm、WPF等单线程UI程序

代码1(旧异步)

private void button1_Click(object sender, EventArgs e)
{
    var request = WebRequest.Create("https://github.com/");
    request.BeginGetResponse(new AsyncCallback(t =>
    {
        //(1)处理请求结果的逻辑必须写这里
        label1.Invoke((Action)(() => { label1.Text = "[旧异步]执行完毕!"; }));//(2)这里跨线程访问UI需要做处理      
    }), null);
}

代码2(同步)

private void button3_Click(object sender, EventArgs e)
{
    HttpClient http = new HttpClient();
    var htmlStr = http.GetStringAsync("https://github.com/").Result;
    //(1)处理请求结果的逻辑可以写这里
    label1.Text = "[同步]执行完毕!";//(2)不在需要做跨线程UI处理了
}

代码3(新异步)

 private async void button2_Click(object sender, EventArgs e)
 {
     HttpClient http = new HttpClient();
     var htmlStr = await http.GetStringAsync("https://github.com/");
     //(1)处理请求结果的逻辑可以写这里
     label1.Text = "[新异步]执行完毕!";//(2)不在需要做跨线程UI处理了
 }

新异步的优势:

  • 平昔不了烦人的回调解和管理理
  • 不会像三只代码一样阻塞UI分界面(形成假死)
  • 不在像旧异步管理后访问UI不在供给做跨线程管理
  • 像使用同步代码同样选用异步(超清晰的逻辑)

 是的,说得再多还不及看看实效图来得实在:(新旧异步UI线程未有阻塞,同步阻塞了UI线程)

体育365网址 1

【思虑】:旧的异步方式是开启了2个新的线程去实行,不会阻塞UI线程。那点很好精通。然而,新的异步看上去和协同差距十分小,为何也不会卡住分界面呢?

【原因】:新异步,在施行await表明式前都是运用UI线程,await表达式后会启用新的线程去推行异步,直到异步施行到位并重回结果,然后再回来UI线程(听新闻说使用了SynchronizationContext;k(SolutionItemsProject);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv四.5.二);k(DevLang-csharp)&rd=true))。所以,await是绝非阻塞UI线程的,也就不会产生分界面包车型客车假死。

【注意】:大家在演示同步代码的时候使用了Result。然,在UI单线程程序中动用Result来使异步代码当1只代码应用是1件很危急的事(起码对于不太驾驭新异步的同窗来讲是这么)。至于实际原因稍候再剖析(哎哎,别跑啊)。

对于WinForm、WPF等单线程UI程序

代码1(旧异步)

private void button1_Click(object sender, EventArgs e)
{
    var request = WebRequest.Create("https://github.com/");
    request.BeginGetResponse(new AsyncCallback(t =>
    {
        //(1)处理请求结果的逻辑必须写这里
        label1.Invoke((Action)(() => { label1.Text = "[旧异步]执行完毕!"; }));//(2)这里跨线程访问UI需要做处理      
    }), null);
}

代码2(同步)

private void button3_Click(object sender, EventArgs e)
{
    HttpClient http = new HttpClient();
    var htmlStr = http.GetStringAsync("https://github.com/").Result;
    //(1)处理请求结果的逻辑可以写这里
    label1.Text = "[同步]执行完毕!";//(2)不在需要做跨线程UI处理了
}

代码3(新异步)

 private async void button2_Click(object sender, EventArgs e)
 {
     HttpClient http = new HttpClient();
     var htmlStr = await http.GetStringAsync("https://github.com/");
     //(1)处理请求结果的逻辑可以写这里
     label1.Text = "[新异步]执行完毕!";//(2)不在需要做跨线程UI处理了
 }

新异步的优势:

  • 并未有了烦人的回调解和管理理
  • 不会像3只代码一样阻塞UI分界面(造成假死)
  • 不在像旧异步管理后访问UI不在须求做跨线程处理
  • 像使用同步代码相同采取异步(超清晰的逻辑)

 是的,说得再多还不及看看实效图来得实际:(新旧异步UI线程未有阻塞,同步阻塞了UI线程)

体育365网址 2

【思量】:旧的异步情势是敞开了3个新的线程去实践,不会阻塞UI线程。这一点很好掌握。然而,新的异步看上去和协助实行分裂十分小,为何也不会堵塞分界面呢?

【原因】:新异步,在实行await表明式前都以利用UI线程,await表明式后会启用新的线程去执行异步,直到异步实践到位并赶回结果,然后再再次回到UI线程(据书上说使用了SynchronizationContext;k(SolutionItemsProject);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv四.五.二);k(DevLang-csharp)&rd=true))。所以,await是平昔不阻塞UI线程的,也就不会促成分界面的装死。

【注意】:我们在示范同步代码的时候利用了Result。然,在UI单线程程序中央银行使Result来使异步代码当2只代码应用是1件很惊险的事(起码对于不太了然新异步的同学来讲是那样)。至于实际原因稍候再深入分析(哎哎,别跑啊)。

对于Web后台服务程序

大概对于后台程序的熏陶未有单线程程序那么直观,但其价值也是至非常大的。且很五个人对新异步存在误会。

【误解】:新异步能够升级Web程序的性情。

【正解】:异步不会进步单次请求结果的光阴,不过足以抓牢Web程序的吞吐量。

一、为何不会升高单次请求结果的小运?

事实上大家从地点示例代码(固然是UI程序的代码)也足以看到。

 体育365网址 3

二、为啥能够抓好Web程序的吞吐量?

那如何是吞吐量呢,也等于理所必然只可以十人还要做客的网址未来能够二十私有同一时候做客了。也便是常说的并发量。

要么用地方的代码来解释。[代码2]
阻塞了UI线程等待请求结果,所以UI线程被占用,而[代码3]使用了新的线程请求,所以UI线程没有被占用,而得以再三再四响应UI界面。

那难题来了,大家的Web程序原始正是多线程的,且web线程都是跑的线程池线程(使用线程池线程是为了制止不断制造、销毁线程所变成的能源资金财产浪费),而线程池线程可应用线程数量是一定的,尽管能够设置,但它仍旧会在料定限制内。如此一来,大家web线程是谈何轻巧的(物以稀为贵),不能够滥用。用完了,那么别的用户请求的时候就无法管理间接50三了。

那怎么着算是滥用呢?例如:文件读取、U智跑L请求、数据库访问等IO请求。假若用web线程来做那么些耗费时间的IO操作那么就能够堵塞web线程,而web线程阻塞得多了web线程池线程就远远不足用了。也就高达了web程序最大访问数。

此时我们的新异步突兀而起,解放了那个原来管理IO请求而围堵的web线程(想偷懒?没门,干活了。)。通过异步方式使用相对廉价的线程(非web线程池线程)来管理IO操作,那样web线程池线程就可以解放出来管理越来越多的央求了。

不信?下边大家来测试下:

【测试步骤】:

一、新建3个web api项目 

2、新建3个数目访问类,分别提供联合、异步方法(在情势逻辑推行前后读取时间、线程id、web线程池线程使用数)

public class GetDataHelper
{
    /// <summary>
    /// 同步方法获取数据
    /// </summary>
    /// <returns></returns>
    public string GetData()
    {
        var beginInfo = GetBeginThreadInfo();
        using (HttpClient http = new HttpClient())
        {
            http.GetStringAsync("https://github.com/").Wait();//注意:这里是同步阻塞
        }
        return beginInfo + GetEndThreadInfo();
    }

    /// <summary>
    /// 异步方法获取数据
    /// </summary>
    /// <returns></returns>
    public async Task<string> GetDataAsync()
    {
        var beginInfo = GetBeginThreadInfo();
        using (HttpClient http = new HttpClient())
        {
            await http.GetStringAsync("https://github.com/");//注意:这里是异步等待
        }
        return beginInfo + GetEndThreadInfo();
    }

    public string GetBeginThreadInfo()
    {
        int t1, t2, t3;
        ThreadPool.GetAvailableThreads(out t1, out t3);
        ThreadPool.GetMaxThreads(out t2, out t3);
        return string.Format("开始:{0:mm:ss,ffff} 线程Id:{1} Web线程数:{2}",
                                DateTime.Now,
                                Thread.CurrentThread.ManagedThreadId,                                  
                                t2 - t1);
    }

    public string GetEndThreadInfo()
    {
        int t1, t2, t3;
        ThreadPool.GetAvailableThreads(out t1, out t3);
        ThreadPool.GetMaxThreads(out t2, out t3);
        return string.Format(" 结束:{0:mm:ss,ffff} 线程Id:{1} Web线程数:{2}",
                                DateTime.Now,
                                Thread.CurrentThread.ManagedThreadId,
                                t2 - t1);
    }
}

三、新建一个web api调控器

[HttpGet]
public async Task<string> Get(string str)
{
    GetDataHelper sqlHelper = new GetDataHelper();
    switch (str)
    {
        case "异步处理"://
            return await sqlHelper.GetDataAsync();
        case "同步处理"://
            return sqlHelper.GetData();
    }
    return "参数不正确";           
}       

4、发布web
api程序,布署到本地iis(联合链接http://localhost:803/api/Home?str=同步处理 
异步链接http://localhost:803/api/Home?str=异步处理

5、接着下面的winform程序里面测试请求:(相同的时候提倡13个请求)

体育365网址 4体育365网址 5

private void button6_Click(object sender, EventArgs e)
{
    textBox1.Text = "";
    label1.Text = "";
    Task.Run(() =>
    {
        TestResultUrl("http://localhost:803/api/Home?str=同步处理");
    });
}

private void button5_Click(object sender, EventArgs e)
{
    textBox1.Text = "";
    label1.Text = "";
    Task.Run(() =>
    {
        TestResultUrl("http://localhost:803/api/Home?str=异步处理");
    });
}

public void TestResultUrl(string url)
{
    int resultEnd = 0;
    HttpClient http = new HttpClient();

    int number = 10;
    for (int i = 0; i < number; i++)
    {
        new Thread(async () =>
        {
            var resultStr = await http.GetStringAsync(url);
            label1.Invoke((Action)(() =>
            {
                textBox1.AppendText(resultStr.Replace(" ", "\r\t") + "\r\n");
                if (++resultEnd >= number)
                {
                    label1.Text = "全部执行完毕";
                }
            }));

        }).Start();
    }
}

View Code

六、重启iis,并用浏览器访问一回要请求的链接地址(预热)

柒、运维winform程序,点击“访问同步实现的Web”:

体育365网址 6

体育365网址 7

八、重复6,然后再次起动winform程序点击“访问异步完成的Web”

体育365网址 8

见到这么些数量有怎么样感想?

数据和大家面前的【正解】完全相符。仔细考查,各样单次请求用时基本上相差非常的小。
不过步骤7″同步达成”最高投入web线程数是拾,而步骤八“异步完结”最高投入web线程数是3。

也正是说“异步达成”使用更加少的web线程实现了同壹的伏乞数量,如此1来大家就有更加多剩余的web线程去管理越多用户发起的伸手。

随着大家还发掘2头完成请求前后的线程ID是均等的,而异步完成上下线程ID不必然1致。再度申明推行await异步前释放了主线程。

【结论】:

  • 接纳新异步能够荣升Web服务程序的吞吐量
  • 对于客户端的话,web服务的异步并不会加强客户端的单次访问速度。
  • 推行新异步前会放出web线程,而等待异步推行到位后又回去了web线程上。从而巩固web线程的利用率。

【图解】:

体育365网址 9

对于Web后台服务程序

或是对于后台程序的影响未有单线程程序那么直观,但其股票总值也是可怜大的。且诸多少人对新异步存在误会。

【误解】:新异步可以荣升Web程序的属性。

【正解】:异步不会升高单次请求结果的大运,但是足以进步Web程序的吞吐量。

壹、为啥不会进步单次请求结果的时间?

实则大家从地方示例代码(即便是UI程序的代码)也得以看出。

 体育365网址 10

二、为啥能够提升Web程序的吞吐量?

那怎么是吞吐量呢,约等于自然只可以12位还要做客的网站今后可以二十一个体同一时候做客了。相当于常说的并发量。

抑或用地点的代码来表明。[代码2]
阻塞了UI线程等待请求结果,所以UI线程被占用,而[代码3]利用了新的线程请求,所以UI线程没有被占用,而得以承继响应UI分界面。

那难题来了,我们的Web程序原始正是四线程的,且web线程都以跑的线程池线程(使用线程池线程是为着防止不断创立、销毁线程所产生的能源资金财产浪费),而线程池线程可利用线程数量是早晚的,固然可以设置,但它依然会在早晚限制内。如此壹来,大家web线程是高雅的(物以稀为贵),无法滥用。用完了,那么别的用户请求的时候就无法管理直接503了。

那怎么算是滥用呢?比方:文件读取、UBMWX五L请求、数据库访问等IO请求。假设用web线程来做这一个耗时的IO操作那么就能够卡住web线程,而web线程阻塞得多了web线程池线程就相当不够用了。也就达到了web程序最大访问数。

此刻大家的新异步崛地而起,解放了那多少个原本管理IO请求而围堵的web线程(想偷懒?没门,干活了。)。通过异步格局使用相对廉价的线程(非web线程池线程)来管理IO操作,那样web线程池线程就能够解放出来管理更加多的乞请了。

不信?下边大家来测试下:

【测试步骤】:

1、新建三个web api项目 

二、新建三个数目访问类,分别提供一块、异步方法(在艺术逻辑试行前后读取时间、线程id、web线程池线程使用数)

public class GetDataHelper
{
    /// <summary>
    /// 同步方法获取数据
    /// </summary>
    /// <returns></returns>
    public string GetData()
    {
        var beginInfo = GetBeginThreadInfo();
        using (HttpClient http = new HttpClient())
        {
            http.GetStringAsync("https://github.com/").Wait();//注意:这里是同步阻塞
        }
        return beginInfo + GetEndThreadInfo();
    }

    /// <summary>
    /// 异步方法获取数据
    /// </summary>
    /// <returns></returns>
    public async Task<string> GetDataAsync()
    {
        var beginInfo = GetBeginThreadInfo();
        using (HttpClient http = new HttpClient())
        {
            await http.GetStringAsync("https://github.com/");//注意:这里是异步等待
        }
        return beginInfo + GetEndThreadInfo();
    }

    public string GetBeginThreadInfo()
    {
        int t1, t2, t3;
        ThreadPool.GetAvailableThreads(out t1, out t3);
        ThreadPool.GetMaxThreads(out t2, out t3);
        return string.Format("开始:{0:mm:ss,ffff} 线程Id:{1} Web线程数:{2}",
                                DateTime.Now,
                                Thread.CurrentThread.ManagedThreadId,                                  
                                t2 - t1);
    }

    public string GetEndThreadInfo()
    {
        int t1, t2, t3;
        ThreadPool.GetAvailableThreads(out t1, out t3);
        ThreadPool.GetMaxThreads(out t2, out t3);
        return string.Format(" 结束:{0:mm:ss,ffff} 线程Id:{1} Web线程数:{2}",
                                DateTime.Now,
                                Thread.CurrentThread.ManagedThreadId,
                                t2 - t1);
    }
}

三、新建一个web api调整器

[HttpGet]
public async Task<string> Get(string str)
{
    GetDataHelper sqlHelper = new GetDataHelper();
    switch (str)
    {
        case "异步处理"://
            return await sqlHelper.GetDataAsync();
        case "同步处理"://
            return sqlHelper.GetData();
    }
    return "参数不正确";           
}       

四、公布web
api程序,陈设到地头iis(共同链接http://localhost:803/api/Home?str=同步处理 
异步链接http://localhost:803/api/Home?str=异步处理

五、接着上面的winform程序里面测试请求:(同不时间提倡十个请求)

体育365网址 11体育365网址 12

private void button6_Click(object sender, EventArgs e)
{
    textBox1.Text = "";
    label1.Text = "";
    Task.Run(() =>
    {
        TestResultUrl("http://localhost:803/api/Home?str=同步处理");
    });
}

private void button5_Click(object sender, EventArgs e)
{
    textBox1.Text = "";
    label1.Text = "";
    Task.Run(() =>
    {
        TestResultUrl("http://localhost:803/api/Home?str=异步处理");
    });
}

public void TestResultUrl(string url)
{
    int resultEnd = 0;
    HttpClient http = new HttpClient();

    int number = 10;
    for (int i = 0; i < number; i++)
    {
        new Thread(async () =>
        {
            var resultStr = await http.GetStringAsync(url);
            label1.Invoke((Action)(() =>
            {
                textBox1.AppendText(resultStr.Replace(" ", "\r\t") + "\r\n");
                if (++resultEnd >= number)
                {
                    label1.Text = "全部执行完毕";
                }
            }));

        }).Start();
    }
}

View Code

6、重启iis,并用浏览器访问一回要请求的链接地址(预热)

七、运营winform程序,点击“访问同步落成的Web”:

体育365网址 13

体育365网址 14

八、重复6,然后再一次开动winform程序点击“访问异步达成的Web”

体育365网址 15

见状那么些数量有哪些感想?

数据和咱们眼下的【正解】完全合乎。仔细考查,每一种单次请求用时基本上相差非常小。
然则步骤7″同步完成”最高投入web线程数是10,而步骤八“异步完结”最高投入web线程数是三。

也正是说“异步实现”使用越来越少的web线程完成了平等的乞请数量,如此壹来大家就有越来越多剩余的web线程去管理更加多用户发起的乞请。

随后大家还开掘一齐达成请求前后的线程ID是大同小异的,而异步达成上下线程ID不确定一致。再度应验施行await异步前释放了主线程。

【结论】:

  • 利用新异步能够荣升Web服务程序的吞吐量
  • 对此客户端的话,web服务的异步并不会巩固客户端的单次访问速度。
  • 推行新异步前会释放web线程,而等待异步执行到位后又回到了web线程上。从而进步web线程的利用率。

【图解】:

体育365网址 16

Result的死锁陷阱

咱俩在分析UI单线程程序的时候说过,要慎用异步的Result属性。上面大家来深入分析:

private void button4_Click(object sender, EventArgs e)
{
    label1.Text = GetUlrString("https://github.com/").Result;
}

public async Task<string> GetUlrString(string url)
{
    using (HttpClient http = new HttpClient())
    {
        return await http.GetStringAsync(url);
    }
}

代码 GetUlrString(“https://github.com/").Result 的Result属性会阻塞(占用)UI线程,而进行到GetUlrString方法的
await异步的时候又要释放UI线程。此时争执就来了,由于线程能源的抢占导致死锁。

且Result属性和.Wait()方法1致会堵塞线程。此等难题在Web服务程序里面一样存在。(区别:UI单次线程程序和web服务程序都会自由主线程,区别的是Web服务线程不一定会回去原本的主线程,而UI程序一定会回到原先的UI线程)

笔者们前边说过,.net为啥会那样智能的自发性释放主线程然后等待异步推行完成后又回到主线程是因为SynchronizationContext;k(SolutionItemsProject);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2);k(DevLang-csharp)&rd=true)的功劳。

但此处有个不等,这就是调整台程序里面是从未有过SynchronizationContext;k(SolutionItemsProject);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv肆.伍.2);k(DevLang-csharp)&rd=true)的。所以这段代码放在调控台里面运维是未曾难点的。

static void Main(string[] args)
{
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    GetUlrString("https://github.com/").Wait();
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    Console.ReadKey();
}

public async static Task<string> GetUlrString(string url)
{
    using (HttpClient http = new HttpClient())
    {
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        return await http.GetStringAsync(url);
    }
}

打字与印刷出来的都是同二个线程ID

Result的死锁陷阱

咱俩在深入分析UI单线程程序的时候说过,要慎用异步的Result属性。上面我们来解析:

private void button4_Click(object sender, EventArgs e)
{
    label1.Text = GetUlrString("https://github.com/").Result;
}

public async Task<string> GetUlrString(string url)
{
    using (HttpClient http = new HttpClient())
    {
        return await http.GetStringAsync(url);
    }
}

代码 GetUlrString(“https://github.com/").Result 的Result属性会阻塞(占用)UI线程,而进行到GetUlrString方法的
await异步的时候又要释放UI线程。此时龃龉就来了,由于线程财富的侵夺导致死锁。

且Result属性和.Wait()方法1致会堵塞线程。此等难点在Web服务程序里面同样存在。(分化:UI单次线程程序和web服务程序都会释放主线程,分歧的是Web服务线程不一定会重临原本的主线程,而UI程序一定会回来原先的UI线程)

大家日前说过,.net为啥会这么智能的自行释放主线程然后等待异步施行完结后又回去主线程是因为SynchronizationContext;k(SolutionItemsProject);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2);k(DevLang-csharp)&rd=true)的功劳。

但这里有个分裂,那就是调整台程序里面是不曾SynchronizationContext;k(SolutionItemsProject);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv四.五.二);k(DevLang-csharp)&rd=true)的。所以这段代码放在调节台里面运转是一直不难点的。

static void Main(string[] args)
{
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    GetUlrString("https://github.com/").Wait();
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    Console.ReadKey();
}

public async static Task<string> GetUlrString(string url)
{
    using (HttpClient http = new HttpClient())
    {
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        return await http.GetStringAsync(url);
    }
}

打字与印刷出来的都以同三个线程ID

选择AsyncHelper在一块儿代码里面调用异步

但可是,可但是,我们亟须在联合签字方法里面实行异步怎办?办法肯定是部分

小编们第二定义八个AsyncHelper静态类:

static class AsyncHelper
{
    private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None,
        TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);

    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
    {
        return _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
    }

    public static void RunSync(Func<Task> func)
    {
        _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
    }
}

接下来调用异步:

private void button7_Click(object sender, EventArgs e)
{
    label1.Text = AsyncHelper.RunSync(() => GetUlrString("https://github.com/"));
}

这样就不会死锁了。

使用AsyncHelper在1块代码里面调用异步

但唯独,可不过,我们必须在1道方法里面推行异步怎办?办法料定是有个别

咱俩先是定义三个AsyncHelper静态类:

static class AsyncHelper
{
    private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None,
        TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);

    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
    {
        return _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
    }

    public static void RunSync(Func<Task> func)
    {
        _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
    }
}

然后调用异步:

private void button7_Click(object sender, EventArgs e)
{
    label1.Text = AsyncHelper.RunSync(() => GetUlrString("https://github.com/"));
}

这么就不会死锁了。

ConfigureAwait

而外AsyncHelper大家仍可以动用Task的ConfigureAwait方法来制止死锁

private void button7_Click(object sender, EventArgs e)
{
    label1.Text = GetUlrString("https://github.com/").Result;
}

public async Task<string> GetUlrString(string url)
{
    using (HttpClient http = new HttpClient())
    {
        return await http.GetStringAsync(url).ConfigureAwait(false);
    }
}

ConfigureAwait的效果:使当前async方法的await后续操作无需复苏到主线程(无需保存线程上下文)。

体育365网址 17

ConfigureAwait

除此而外AsyncHelper我们还是能动用Task的ConfigureAwait方法来幸免死锁

private void button7_Click(object sender, EventArgs e)
{
    label1.Text = GetUlrString("https://github.com/").Result;
}

public async Task<string> GetUlrString(string url)
{
    using (HttpClient http = new HttpClient())
    {
        return await http.GetStringAsync(url).ConfigureAwait(false);
    }
}

ConfigureAwait的成效:使近来async方法的await后续操作不供给恢复生机到主线程(无需保存线程上下文)。

体育365网址 18

那么些管理

关于新异步里面抛出特别的不利姿势。我们先来看上边1段代码:

private async void button8_Click(object sender, EventArgs e)
{
    Task<string> task = GetUlrStringErr(null);
    Thread.Sleep(1000);//一段逻辑。。。。
    textBox1.Text = await task;
}

public async Task<string> GetUlrStringErr(string url)
{
    if (string.IsNullOrWhiteSpace(url))
    {
        throw new Exception("url不能为空");
    }
    using (HttpClient http = new HttpClient())
    {
        return await http.GetStringAsync(url);
    }
}

调治推行实践流程:

体育365网址 19

在实践完11八行的时候依然从未把那么些抛出来?那不是逆天了吗。非得在伺机await实施的时候才报错,分明11玖行的逻辑试行是不曾什么样含义的。让大家把特别提前抛出:

体育365网址 20

领到1个办法来做表达,那样就会立刻的抛出非常了。有朋友会说这么的太坑爹了呢,二个表明还必须别的写个艺术。接下来我们提供三个平昔不比此坑爹的秘诀:

体育365网址 21

在异步函数里面用无名氏异步函数进行李包裹装,同样能够完毕即时验证。

以为也比不上前种情势很多少…可是能如何是好呢。

非常管理

关于新异步里面抛出卓殊的不错姿势。大家先来看上面一段代码:

private async void button8_Click(object sender, EventArgs e)
{
    Task<string> task = GetUlrStringErr(null);
    Thread.Sleep(1000);//一段逻辑。。。。
    textBox1.Text = await task;
}

public async Task<string> GetUlrStringErr(string url)
{
    if (string.IsNullOrWhiteSpace(url))
    {
        throw new Exception("url不能为空");
    }
    using (HttpClient http = new HttpClient())
    {
        return await http.GetStringAsync(url);
    }
}

调养推行实行流程:

体育365网址 22

在实践完11八行的时候竟然从未把特别抛出来?那不是逆天了呢。非得在等候await实施的时候才报错,显著11玖行的逻辑试行是从未什么样含义的。让我们把非常提前抛出:

体育365网址 23

领取1个艺术来做验证,那样就会即时的抛出极度了。有相爱的人会说那样的太坑爹了吧,3个证实还非得其它写个主意。接下来我们提供二个尚未如此坑爹的主意:

体育365网址 24

在异步函数里面用佚名异步函数举办李包裹装,一样能够兑现即时验证。

备感也比不上前种方法大多少…可是能如何是好呢。

异步的贯彻

地点轻松分析了新异步才能和特性。接下来让我们承袭揭秘异步的面目,神秘的T恤上面毕竟是怎么落到实处的。

第3大家编辑1个用来反编写翻译的演示:

class MyAsyncTest
{
    public async Task<string> GetUrlStringAsync(HttpClient http, string url, int time)
    {
        await Task.Delay(time);
        return await http.GetStringAsync(url);
    }
}

反编写翻译代码:

点击看大图

为了便利阅读,大家把编译器自动命名的类型重命名。

 GetUrlStringAsync 方法成为了那般形容:

public Task<string> GetUrlStringAsync(HttpClient http, string url, int time)
{
    GetUrlStringAsyncdStateMachine stateMachine = new GetUrlStringAsyncdStateMachine()
    {
        _this = this,
        http = http,
        url = url,
        time = time,
        _builder = AsyncTaskMethodBuilder<string>.Create(),
        _state = -1
    };
    stateMachine._builder.Start(ref stateMachine);
    return stateMachine._builder.Task;
}

措施签名完全一致,只是当中的内容形成了1个情形机 GetUrlStringAsyncdStateMachine
 的调用。此状态机正是编写翻译器自动创设的。下边来看望神秘的状态机是怎么着鬼:

private sealed class GetUrlStringAsyncdStateMachine : IAsyncStateMachine
{
    public int _state;
    public MyAsyncTest _this;
    private string _str1;
    public AsyncTaskMethodBuilder<string> _builder;
    private TaskAwaiter taskAwaiter1;
    private TaskAwaiter<string> taskAwaiter2;

    //异步方法的三个形参都到这里来了
    public HttpClient http;
    public int time;
    public string url;

    private void MoveNext()
    {
        string str;
        int num = this._state;
        try
        {
            TaskAwaiter awaiter;
            MyAsyncTest.GetUrlStringAsyncdStateMachine d__;
            string str2;
            switch (num)
            {
                case 0:
                    break;

                case 1:
                    goto Label_00CD;

                default:
                    //这里是异步方法 await Task.Delay(time);的具体实现
                    awaiter = Task.Delay(this.time).GetAwaiter();
                    if (awaiter.IsCompleted)
                    {
                        goto Label_0077;
                    }
                    this._state = num = 0;
                    this.taskAwaiter1 = awaiter;
                    d__ = this;
                    this._builder.AwaitUnsafeOnCompleted<TaskAwaiter, MyAsyncTest.GetUrlStringAsyncdStateMachine>(ref awaiter, ref d__);
                    return;
            }
            awaiter = this.taskAwaiter1;
            this.taskAwaiter1 = new TaskAwaiter();
            this._state = num = -1;
        Label_0077:
            awaiter.GetResult();
            awaiter = new TaskAwaiter();
            //这里是异步方法await http.GetStringAsync(url);的具体实现
            TaskAwaiter<string> awaiter2 = this.http.GetStringAsync(this.url).GetAwaiter();
            if (awaiter2.IsCompleted)
            {
                goto Label_00EA;
            }
            this._state = num = 1;
            this.taskAwaiter2 = awaiter2;
            d__ = this;
            this._builder.AwaitUnsafeOnCompleted<TaskAwaiter<string>, MyAsyncTest.GetUrlStringAsyncdStateMachine>(ref awaiter2, ref d__);
            return;
        Label_00CD:
            awaiter2 = this.taskAwaiter2;
            this.taskAwaiter2 = new TaskAwaiter<string>();
            this._state = num = -1;
        Label_00EA:
            str2 = awaiter2.GetResult();
            awaiter2 = new TaskAwaiter<string>();
            this._str1 = str2;
            str = this._str1;
        }
        catch (Exception exception)
        {
            this._state = -2;
            this._builder.SetException(exception);
            return;
        }
        this._state = -2;
        this._builder.SetResult(str);
    }

    [DebuggerHidden]
    private void SetStateMachine(IAsyncStateMachine stateMachine)
    {
    }

}

引人注目四个异步等待推行的时候固然在任何时间任何地点调用状态机中的MoveNext()方法。经验来至我们前边剖判过的IEumerable,但是前日的这些肯定复杂度要超越以前的丰富。测度是那样,大家依然来申明下实际:

在初步方法 GetUrlStringAsync 第二遍运转状态机 stateMachine._builder.Start(ref stateMachine); 

体育365网址 25

 确实是调用了 MoveNext 。因为_state的初阶值是-一,所以进行到了上面包车型客车地方:

体育365网址 26

绕了壹圈又赶回了 MoveNext 。因此,大家得以现象成三个异步调用就是在相连实践MoveNext直到甘休。

说了这么久有哪些看头吧,就如忘记了大家的目标是要因而事先编写的测试代码来深入分析异步的实践逻辑的。

重新贴出此前的测试代码,防止忘记了。

体育365网址 27

反编写翻译后代码实践逻辑图:

体育365网址 28

不容置疑这只是可能十分大的实践流程,但也可以有 awaiter.伊斯科mpleted 为 true 的情景。其余可能的留着大家温馨去研究吧。 

 

本文已协同至索引目录:《C#基础知识加强

本文demo:https://github.com/zhaopeiym/BlogDemoCode

 

【推荐】

http://www.cnblogs.com/wisdomqq/archive/2012/03/29/2417723.html

 

异步的完结

下边轻易解析了新异步本领和性质。接下来让大家继续揭秘异步的面目,神秘的外衣下边究竟是怎么落到实处的。

体育365网址,先是大家编辑1个用来反编写翻译的示范:

class MyAsyncTest
{
    public async Task<string> GetUrlStringAsync(HttpClient http, string url, int time)
    {
        await Task.Delay(time);
        return await http.GetStringAsync(url);
    }
}

反编译代码:

点击看大图

为了方便阅读,大家把编译器自动命名的种类重命名。

 GetUrlStringAsync 方法成为了那样形容:

public Task<string> GetUrlStringAsync(HttpClient http, string url, int time)
{
    GetUrlStringAsyncdStateMachine stateMachine = new GetUrlStringAsyncdStateMachine()
    {
        _this = this,
        http = http,
        url = url,
        time = time,
        _builder = AsyncTaskMethodBuilder<string>.Create(),
        _state = -1
    };
    stateMachine._builder.Start(ref stateMachine);
    return stateMachine._builder.Task;
}

格局具名完全1致,只是当中的内容形成了多少个气象机 GetUrlStringAsyncdStateMachine
 的调用。此状态机就是编译器自动创设的。上面来探视神秘的状态机是什么样鬼:

private sealed class GetUrlStringAsyncdStateMachine : IAsyncStateMachine
{
    public int _state;
    public MyAsyncTest _this;
    private string _str1;
    public AsyncTaskMethodBuilder<string> _builder;
    private TaskAwaiter taskAwaiter1;
    private TaskAwaiter<string> taskAwaiter2;

    //异步方法的三个形参都到这里来了
    public HttpClient http;
    public int time;
    public string url;

    private void MoveNext()
    {
        string str;
        int num = this._state;
        try
        {
            TaskAwaiter awaiter;
            MyAsyncTest.GetUrlStringAsyncdStateMachine d__;
            string str2;
            switch (num)
            {
                case 0:
                    break;

                case 1:
                    goto Label_00CD;

                default:
                    //这里是异步方法 await Task.Delay(time);的具体实现
                    awaiter = Task.Delay(this.time).GetAwaiter();
                    if (awaiter.IsCompleted)
                    {
                        goto Label_0077;
                    }
                    this._state = num = 0;
                    this.taskAwaiter1 = awaiter;
                    d__ = this;
                    this._builder.AwaitUnsafeOnCompleted<TaskAwaiter, MyAsyncTest.GetUrlStringAsyncdStateMachine>(ref awaiter, ref d__);
                    return;
            }
            awaiter = this.taskAwaiter1;
            this.taskAwaiter1 = new TaskAwaiter();
            this._state = num = -1;
        Label_0077:
            awaiter.GetResult();
            awaiter = new TaskAwaiter();
            //这里是异步方法await http.GetStringAsync(url);的具体实现
            TaskAwaiter<string> awaiter2 = this.http.GetStringAsync(this.url).GetAwaiter();
            if (awaiter2.IsCompleted)
            {
                goto Label_00EA;
            }
            this._state = num = 1;
            this.taskAwaiter2 = awaiter2;
            d__ = this;
            this._builder.AwaitUnsafeOnCompleted<TaskAwaiter<string>, MyAsyncTest.GetUrlStringAsyncdStateMachine>(ref awaiter2, ref d__);
            return;
        Label_00CD:
            awaiter2 = this.taskAwaiter2;
            this.taskAwaiter2 = new TaskAwaiter<string>();
            this._state = num = -1;
        Label_00EA:
            str2 = awaiter2.GetResult();
            awaiter2 = new TaskAwaiter<string>();
            this._str1 = str2;
            str = this._str1;
        }
        catch (Exception exception)
        {
            this._state = -2;
            this._builder.SetException(exception);
            return;
        }
        this._state = -2;
        this._builder.SetResult(str);
    }

    [DebuggerHidden]
    private void SetStateMachine(IAsyncStateMachine stateMachine)
    {
    }

}

显著三个异步等待实行的时候就是在不停调用状态机中的MoveNext()方法。经验来至大家事先分析过的IEumerable,然如今日的那几个显然复杂度要高于在此以前的百般。推测是这么,我们照旧来证实下实际:

在胚胎方法 GetUrlStringAsync 第一遍开发银行状态机 stateMachine._builder.Start(ref stateMachine); 

体育365网址 29

 确实是调用了 MoveNext 。因为_state的伊始值是-壹,所以实行到了上面包车型客车岗位:

体育365网址 30

绕了一圈又回去了 MoveNext 。因而,我们能够现象成多个异步调用就是在频频奉行MoveNext直到甘休。

说了这么久有啥意思吧,仿佛忘记了大家的目标是要通过事先编写的测试代码来剖判异步的施行逻辑的。

再也贴出以前的测试代码,避防忘记了。

体育365网址 31

反编写翻译后代码执行逻辑图:

体育365网址 32

当然那只是或者异常的大的进行流程,但也有 awaiter.伊斯科mpleted 为 true 的情景。别的恐怕的留着大家温馨去雕饰吧。 

 

正文已联手至索引目录:《C#基础知识加强

本文demo:https://github.com/zhaopeiym/BlogDemoCode

 

【推荐】

http://www.cnblogs.com/wisdomqq/archive/2012/03/29/2417723.html

 

相关文章