异步的社会风气爱博体育app

【转】C#异步的社会风气【下】

【转】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线程)

爱博体育app 1

【思考】:旧的异步形式是敞开了一个新的线程去执行,不会阻塞UI线程。那一点很好精晓。不过,新的异步看上去和一起分歧不大,为啥也不会卡住界面呢?

【原因】:新异步,在举办await表明式前都以行使UI线程,await表明式后会启用新的线程去实施异步,直到异步执行到位并回到结果,然后再回到UI线程(听说使用了SynchronizationContext;k(SolutionItemsProject);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2);k(DevLang-csharp)&rd=true))。所以,await是没有阻塞UI线程的,也就不会招致界面的装死。

【注意】:我们在示范同步代码的时候利用了Result。然,在UI单线程程序中拔取Result来使异步代码当一头代码应用是一件很惊险的事(起码对于不太精晓新异步的同窗来说是如此)。至于具体原因稍候再分析(哎哎,别跑啊)。

对于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线程)

爱博体育app 2

【思考】:旧的异步形式是开启了一个新的线程去履行,不会阻塞UI线程。这一点很好理解。可是,新的异步看上去和同步差异不大,为何也不会堵塞界面呢?

【原因】:新异步,在执行await表明式前都以使用UI线程,await表达式后会启用新的线程去执行异步,直到异步执行到位并重回结果,然后再回到UI线程(据书上说使用了SynchronizationContext;k(SolutionItemsProject);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2);k(DevLang-csharp)&rd=true))。所以,await是从未有过阻塞UI线程的,也就不会导致界面的假死。

【注意】:大家在示范同步代码的时候使用了Result。然,在UI单线程程序中拔取Result来使异步代码当一头代码应用是一件很惊险的事(起码对于不太了然新异步的校友来说是这么)。至于实际原因稍候再分析(哎哎,别跑啊)。

对此Web后台服务程序

或者对于后台程序的熏陶没有单线程程序那么直观,但其市值也是格外大的。且很五个人对新异步存在误会。

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

【正解】:异步不会进步单次请求结果的小时,但是足以坚实Web程序的吞吐量。

1、为啥不会升级单次请求结果的日子?

实在大家从上边示例代码(即便是UI程序的代码)也得以看来。

 爱博体育app 3

2、为何可以增加Web程序的吞吐量?

那怎么是吞吐量呢,也等于自然只好十个人还要做客的网站未来可以二十私家同时做客了。约等于常说的并发量。

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

那难题来了,大家的Web程序原始就是十六线程的,且web线程都以跑的线程池线程(使用线程池线程是为了防止不断成立、销毁线程所造成的资源资产浪费),而线程池线程可拔取线程数量是必然的,即便可以安装,但它如故会在早晚限制内。如此一来,大家web线程是难能可贵的(物以稀为贵),不只怕滥用。用完了,那么其余用户请求的时候就不可以处理直接503了。

这怎样算是滥用呢?比如:文件读取、ULANDL请求、数据库访问等IO请求。如若用web线程来做这几个耗时的IO操作那么就会阻塞web线程,而web线程阻塞得多了web线程池线程就不够用了。也就高达了web程序最大访问数。

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

不信?上面大家来测试下:

【测试步骤】:

1、新建一个web api项目 

2、新建一个数据访问类,分别提供联合、异步方法(在章程逻辑执行前后读取时间、线程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);
    }
}

3、新建一个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程序里面测试请求:(同时提倡10个请求)

爱博体育app 4爱博体育app 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

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

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

爱博体育app 6

爱博体育app 7

8、重复6,然后重新启航winform程序点击“访问异步完成的Web”

爱博体育app 8

探望这几个数据有何样感想?

数量和大家前边的【正解】完全合乎。仔细察看,逐个单次请求用时基本上相差不大。
不过步骤7″同步完成”最高投入web线程数是10,而步骤8“异步达成”最高投入web线程数是3。

相当于说“异步达成”使用更少的web线程已毕了同样的伏乞数量,如此一来我们就有越来越多剩余的web线程去处理越来越多用户发起的央求。

继之大家还发现一头完毕请求前后的线程ID是如出一辙的,而异步落成上下线程ID不肯定一致。再度注脚执行await异步前释放了主线程。

【结论】:

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

【图解】:

爱博体育app 9

对于Web后台服务程序

恐怕对于后台程序的影响没有单线程程序那么直观,但其价值也是格外大的。且很多少人对新异步存在误解。

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

【正解】:异步不会升级单次请求结果的时辰,但是可以进步Web程序的吞吐量。

1、为什么不会升级单次请求结果的时日?

实质上大家从上边示例代码(纵然是UI程序的代码)也得以看到。

 爱博体育app 10

2、为何可以加强Web程序的吞吐量?

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

抑或用地点的代码来表达。[代码2]
阻塞了UI线程等待请求结果,所以UI线程被占用,而[代码3]动用了新的线程请求,所以UI线程没有被占用,而可以连续响应UI界面。

那难点来了,我们的Web程序原始就是二十四线程的,且web线程都是跑的线程池线程(使用线程池线程是为着防止不断开创、销毁线程所导致的财富开支浪费),而线程池线程可应用线程数量是毫无疑问的,固然可以设置,但它照旧会在任其自然范围内。如此一来,大家web线程是爱抚的(物以稀为贵),不或许滥用。用完了,那么其余用户请求的时候就不能处理直接503了。

那什么样算是滥用呢?比如:文件读取、U奔驰M级L请求、数据库访问等IO请求。假若用web线程来做那些耗时的IO操作那么就会堵塞web线程,而web线程阻塞得多了web线程池线程就不够用了。也就直达了web程序最大访问数。

那会儿我们的新异步横空出世,解放了那么些原来处理IO请求而堵塞的web线程(想偷懒?没门,干活了。)。通过异步方式使用相对廉价的线程(非web线程池线程)来处理IO操作,那样web线程池线程就可以解放出来处理越来越多的请求了。

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

【测试步骤】:

1、新建一个web api项目 

2、新建一个数额访问类,分别提供联合、异步方法(在艺术逻辑执行前后读取时间、线程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);
    }
}

3、新建一个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程序里面测试请求:(同时提倡10个请求)

爱博体育app 11爱博体育app 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,并用浏览器访问三次要伏乞的链接地址(预热)

7、运行winform程序,点击“访问同步已毕的Web”:

爱博体育app 13

爱博体育app 14

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

爱博体育app 15

观看这个多少有怎么样感想?

多少和大家面前的【正解】完全相符。仔细考察,逐个单次请求用时基本上相差不大。
然而步骤7″同步完毕”最高投入web线程数是10,而步骤8“异步完结”最高投入web线程数是3。

也等于说“异步完成”使用更少的web线程完毕了一如既往的哀求数量,如此一来我们就有越多剩余的web线程去处理越多用户发起的呼吁。

接着大家还发现一起已毕请求前后的线程ID是同样的,而异步完成上下线程ID不肯定一致。再次验证执行await异步前释放了主线程。

【结论】:

  • 行使新异步可以升级Web服务程序的吞吐量
  • 对于客户端的话,web服务的异步并不会增高客户端的单次访问速度。
  • 推行新异步前会释放web线程,而等待异步执行到位后又再次回到了web线程上。从而抓牢web线程的利用率。

【图解】:

爱博体育app 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()方法一致会阻塞线程。此等难题在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%3Dv4.5.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()方法一致会卡住线程。此等难题在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%3Dv4.5.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

运用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在一块代码里面调用异步

但只是,可可是,大家必须在一道方法里面实践异步怎办?办法肯定是一些

咱俩先是定义一个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后续操作不须要还原到主线程(不需求保存线程上下文)。

爱博体育app 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后续操作不须要恢复生机到主线程(不须求保存线程上下文)。

爱博体育app 18

老大处理

关于新异步里面抛出非凡的正确性姿势。大家先来看下边一段代码:

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);
    }
}

调节执行实施流程:

爱博体育app 19

在推行完118行的时候居然从未把尤其抛出来?这不是逆天了吧。非得在守候await执行的时候才报错,分明119行的逻辑执行是没有何意思的。让我们把分外提前抛出:

爱博体育app 20

领取一个主意来做声明,那样就能马上的抛出十分了。有心上人会说那样的太坑爹了啊,一个阐明还非得别的写个点子。接下来大家提供一个从未那样坑爹的方法:

爱博体育app 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);
    }
}

调节执行实施流程:

爱博体育app 22

在举办完118行的时候依然从未把那多少个抛出来?那不是逆天了啊。非得在守候await执行的时候才报错,显然119行的逻辑执行是尚未怎么意义的。让我们把非凡提前抛出:

爱博体育app 23

领取一个主意来做表达,那样就能立即的抛出非凡了。有意中人会说这么的太坑爹了吗,一个认证还必须其余写个章程。接下来大家提供一个并未那样坑爹的措施:

爱博体育app 24

在异步函数里面用匿名异步函数进行打包,同样可以完结即时验证。

觉得也不比前种办法好多少…不过能咋做呢。

异步的落实

地点简单分析了新异步能力和质量。接下来让咱们后续揭秘异步的天柱山真面目,神秘的外衣上边终究是怎么落实的。

第一大家编辑一个用来反编译的示范:

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;
}

办法签名完全一致,只是其中的始末变成了一个情形机 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); 

爱博体育app 25

 确实是调用了 MoveNext 。因为_state的初阶值是-1,所以举办到了上边的职位:

爱博体育app 26

绕了一圈又回去了 MoveNext 。因而,大家得以现象成四个异步调用就是在时时刻刻实施MoveNext直到为止。

说了这么久有何看头吧,就如忘记了俺们的目的是要经过事先编写的测试代码来分析异步的执行逻辑的。

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

爱博体育app 27

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

爱博体育app 28

当然那只是或然较大的施行流程,但也有 awaiter.Iscompleted 为 true 的气象。其余或者的留着我们自身去雕饰吧。 

 

正文已同步至索引目录:《C#基础知识巩固

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

 

【推荐】

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

 

异步的完成

上边不难分析了新异步能力和属性。接下来让大家后续揭秘异步的本来面目,神秘的外衣上面毕竟是怎么落实的。

先是大家编辑一个用来反编译的演示:

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;
}

方式签名完全一致,只是其中的内容变成了一个情景机 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); 

爱博体育app 29

 确实是调用了 MoveNext 。因为_state的开始值是-1,所以进行到了上边的岗位:

爱博体育app 30

绕了一圈又再次回到了 MoveNext 。因此,大家得以现象成多少个异步调用就是在不断推行MoveNext直到为止。

说了这么久有如何意思吧,就像忘记了我们的目标是要通过事先编写的测试代码来分析异步的施行逻辑的。

重复贴出以前的测试代码,以防忘记了。

爱博体育app 31

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

爱博体育app 32

理所当然这只是可能较大的施行流程,但也有 awaiter.Iscompleted 为 true 的景况。其他只怕的留着大家本身去探究吧。 

 

正文已一起至索引目录:《C#基础知识巩固

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

 

【推荐】

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

 

相关文章