亚洲必赢手机【转】C#异步的社会风气【下】【转】C#异步的世界【下】

by admin on 2018年10月5日

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

亚洲必赢手机 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线程)

亚洲必赢手机 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程序的代码)也可看出。

 亚洲必赢手机 3

2、为什么可以提高Web程序的吞吐量?

那么什么是吞吐量为,也即是本只能十个人而做客的网站现可以二十私以做客了。也就算是常事说的连发量。

还是用者的代码来解释。[代码2]
阻塞了UI线程等待请求结果,所以UI线程被占用,而[代码3]采取了初的线程请求,所以UI线程没有吃占用,而好连续响应UI界面。

这就是说问题来了,我们的Web程序原始就是多线程的,且web线程都是飞的线程池线程(使用线程池线程是为了避免不断开创、销毁线程所导致的资源成本浪费),而线程池线程可使用线程数量是自然之,尽管可以设置,但其还是碰头以大势所趋范围外。如此一来,我们web线程是珍贵的(物以稀为贵),不克滥用。用了了,那么其他用户请求的下就无法处理直接503了。

那么什么算是滥用呢?比如:文件读取、URL请求、数据库访问等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个请求)

亚洲必赢手机 4亚洲必赢手机 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”:

亚洲必赢手机 6

亚洲必赢手机 7

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

亚洲必赢手机 8

总的来看这些多少产生什么感想?

多少和咱们前面的【正解】完全可。仔细观察,每个单次请求用时大都相差不生。
但是步骤7″同步实现”最高投入web线程数是10,而步骤8“异步实现”最高投入web线程数是3。

也就是说“异步实现”使用还少之web线程完成了同样的恳求数量,如此一来我们尽管生出再多剩余的web线程去处理又多用户发起的求。

继之我们尚发现并实现请求前后的线程ID是同的,而异步实现内外线程ID不自然同。再次应验执行await异步前纵了主线程。

【结论】:

  • 运用新异步可以升级Web服务程序的吞吐量
  • 于客户端的话,web服务之异步并无见面增进客户端的单次访问速度。
  • 施行新异步前会见放出web线程,而待异步执行就后而赶回了web线程上。从而增强web线程的利用率。

【图解】:

亚洲必赢手机 9

于Web后高服务程序

或许对后台程序的熏陶没有单线程程序那么直观,但彼价值吧是那个可怜之。且多丁对新异步存在误解。

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

【正解】:异步不会见升级单次请求结果的岁月,但是可以增强Web程序的吞吐量。

1、为什么不见面提升单次请求结果的辰?

骨子里我们打者示例代码(虽然是UI程序的代码)也堪看到。

 亚洲必赢手机 10

2、为什么可以增进Web程序的吞吐量?

这就是说什么是吞吐量也,也尽管是当只能十独人口又做客的网站现足二十私家以做客了。也就是经常说之并发量。

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

那么问题来了,我们的Web程序原始就是是多线程的,且web线程都是跑的线程池线程(使用线程池线程是为避免不断创造、销毁线程所造成的资源资产浪费),而线程池线程可采用线程数量是大势所趋之,尽管可以装,但它还是会见于一定范围外。如此一来,我们web线程是难得的(物以稀为贵),不可知滥用。用完了,那么其他用户请求的时段就是无法处理直接503了。

这就是说什么算是滥用呢?比如:文件读取、URL请求、数据库访问等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独请求)

亚洲必赢手机 11亚洲必赢手机 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”:

亚洲必赢手机 13

亚洲必赢手机 14

8、重复6,然后再次起动winform程序点击“访问异步实现之Web”

亚洲必赢手机 15

看看这些数量发生什么感想?

数据以及咱们面前的【正解】完全相符。仔细察看,每个单次请求用时差不多相差不充分。
但是步骤7″同步实现”最高投入web线程数是10,而步骤8“异步实现”最高投入web线程数是3。

也就是说“异步实现”使用重复不见的web线程完成了相同的求数量,如此一来我们就发出再次多剩余的web线程去处理还多用户发起的请求。

继我们还发现一起实现请求前后的线程ID是一律的,而异步实现内外线程ID不肯定同。再次印证执行await异步前放了主线程。

【结论】:

  • 以初异步可以荣升Web服务程序的吞吐量
  • 对客户端的话,web服务的异步并无见面提高客户端的单次访问速度。
  • 施行新异步前会自由web线程,而等异步执行得后又返了web线程上。从而提高web线程的利用率。

【图解】:

亚洲必赢手机 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后续操作不待还原至主线程(不需要保存线程上下文)。

亚洲必赢手机 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后续操作不欲恢复到主线程(不欲保存线程上下文)。

亚洲必赢手机 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);
    }
}

调剂执行实施流程:

亚洲必赢手机 19

每当尽了118行之时候竟然没把非常抛出来?这不是逆天了也。非得在等候await执行之早晚才报错,显然119行的逻辑执行是从未呀含义之。让我们管生提前抛出:

亚洲必赢手机 20

领到一个艺术来开说明,这样就算可知即刻的摒弃来特别了。有朋友见面说这么的太坑爹了咔嚓,一个证还非得另外写个章程。接下来我们提供一个无这么坑爹的办法:

亚洲必赢手机 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);
    }
}

调剂执行实施流程:

亚洲必赢手机 22

在执行了118实践的时居然没把大抛出来?这不是逆天了吗。非得在守候await执行之时段才报错,显然119行的逻辑执行是没有呀含义之。让咱们管万分提前抛出:

亚洲必赢手机 23

领到一个措施来举行验证,这样便会及时的废弃来深了。有意中人见面说这样的卓绝坑爹了吧,一个征还得另外写单法子。接下来我们提供一个从未这么坑爹的方:

亚洲必赢手机 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); 

亚洲必赢手机 25

 确实是调用了 MoveNext 。因为_state的初始值是-1,所以实行及了脚的位置:

亚洲必赢手机 26

绕了同样缠而回了 MoveNext 。由此,我们可现象成为多单异步调用就是于不断实践MoveNext直到了。

说了这般绵长发生什么意思呢,似乎忘记了咱的目的是一旦经过前编写的测试代码来分析异步的履逻辑的。

又贴出之前的测试代码,以免忘记了。

亚洲必赢手机 27

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

亚洲必赢手机 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); 

亚洲必赢手机 29

 确实是调用了 MoveNext 。因为_state的初始值是-1,所以实行及了脚的职位:

亚洲必赢手机 30

绕了同圈而回来了 MoveNext 。由此,我们可现象成为多个异步调用就是在连实践MoveNext直到了。

说了这般久发生什么意思呢,似乎忘记了咱的目的是要经过前编写的测试代码来分析异步的履逻辑的。

再贴出之前的测试代码,以免忘记了。

亚洲必赢手机 31

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

亚洲必赢手机 32

自然就就是可能比较生之执行流程,但为来 awaiter.Iscompleted 为 true 的状态。其他可能的养在大家温馨失去琢磨吧。 

 

正文就联合至索引目录:《C#基础知识巩固》

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

 

【推荐】

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

 

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图