最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺談C# async await 死鎖問題總結(jié)

 更新時(shí)間:2020年10月13日 11:55:06   作者:Bob1227  
這篇文章主要介紹了淺談C# async await 死鎖問題總結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

可能發(fā)生死鎖的程序類型

1、WPF/WinForm程序

2、asp.net (不包括asp.net core)程序

死鎖的產(chǎn)生原理

對(duì)異步方法返回的Task調(diào)用Wait()或訪問Result屬性時(shí),可能會(huì)產(chǎn)生死鎖。

下面的WPF代碼會(huì)出現(xiàn)死鎖:

private void Button_Click_7(object sender, RoutedEventArgs e)
    {
      Method1().Wait();
    }

    private async Task Method1()
    {
      await Task.Delay(100);

      txtLog.AppendText("后續(xù)代碼");
    }

下面的asp.net mvc代碼也會(huì)出現(xiàn)死鎖:

public ActionResult Index()
    {
      string s=Method1().Result;

      return View();
    }

    private async Task<string> Method1()
    {
      await Task.Delay(100);

      return "hello";
    }

以WPF代碼為例,事件處理器調(diào)用Method1,得到Task對(duì)象,然后調(diào)用Task的Wait方法,阻塞自己所在的線程,即主線程,直到Task對(duì)象“完成”。而返回的Task對(duì)象要想“完成”,必須在主線程上執(zhí)行await之后的代碼。而主線程早就處于阻塞狀態(tài),它在等待Task對(duì)象完成!于是死鎖就產(chǎn)生了。

asp.net mvc代碼是同樣的道理。

什么時(shí)候必然會(huì)死鎖,如何避免

從上面的兩個(gè)例子中似乎可以得出結(jié)論:在WPF/WinForm/asp.net程序中,在異步方法上調(diào)用.Result/Wait(),就會(huì)產(chǎn)生死鎖。然而事實(shí)并非如此。

如下面的WPF代碼就不會(huì)出現(xiàn)死鎖:(從web獲取數(shù)據(jù)并顯示在文本框中。此代碼僅為舉例說明,異步事件處理器才是正道)

private void Button_Click_8(object sender, RoutedEventArgs e)
    {
      HttpClient httpClient = new HttpClient();
      httpClient.BaseAddress = new Uri("https://www.baidu.com/");

      string html = httpClient.GetStringAsync("/").Result;      html = "【" + html + "】";

      txtLog.AppendText(html);
    }

把獲取數(shù)據(jù)的代碼摘出來吧:

private void Button_Click_8(object sender, RoutedEventArgs e)
    {
      string html = GetHtml();

      txtLog.AppendText(html);
    }

    private string GetHtml()
    {
      HttpClient httpClient = new HttpClient();
      httpClient.BaseAddress = new Uri("https://www.baidu.com/");

      string html=httpClient.GetStringAsync("/").Result;       html = "【" + html + "】";             return html;
    }

完全沒問題,這是肯定的。

GetHtml()可以寫成異步方法,再改一下:

private void Button_Click_8(object sender, RoutedEventArgs e)
    {
      string html = GetHtml().Result;

      txtLog.AppendText(html);
    }

    private async Task<string> GetHtml()
    {
      HttpClient httpClient = new HttpClient();
      httpClient.BaseAddress = new Uri("https://www.baidu.com/");
      string html=await httpClient.GetStringAsync("/");              
      html = "【" + html + "】";
      return html;    }

(HttpClient的GetStringAsync()方法是異步方法,我們調(diào)用它,然后用async/await的方式創(chuàng)建了一個(gè)自己的異步方法。先不“一路異步到底(Async All the Way)”。)

運(yùn)行一下,死鎖出現(xiàn)了。

為什么在HttpClient的GetStringAsync()方法上執(zhí)行.Result不會(huì)死鎖,而在自己寫的異步方法上執(zhí)行.Result,就出現(xiàn)了死鎖?難道HttpClient的GetStringAsync()方法內(nèi)部有什么特殊的處理?

看一下mono的HttpClient源代碼,可以發(fā)現(xiàn):

所有await 表達(dá)式后面,都加了ConfigureAwait (false),如

return await resp.Content.ReadAsStringAsync ().ConfigureAwait (false);

而由Task的msdn文檔可以知,ConfigureAwait (false)會(huì)指示await之后的代碼不在原先的context (可理解為線程)上運(yùn)行。

修改一下GetHtml()異步方法的代碼:

private void Button_Click_8(object sender, RoutedEventArgs e)
    {
      string html = GetHtml().Result;

      txtLog.AppendText(html);
    }
    private async Task<string> GetHtml()
    {
      HttpClient httpClient = new HttpClient();
      httpClient.BaseAddress = new Uri("https://www.baidu.com/");
      string html=await httpClient.GetStringAsync("/").ConfigureAwait(false);              
      html = "【" + html + "】";
      return html;    }

可以發(fā)現(xiàn),死鎖不會(huì)出現(xiàn)了。

分析:GetHtml()被調(diào)用后,主線程阻塞,等待Task對(duì)象“完成”;HttpClient獲取數(shù)據(jù)完畢,在另外的線程上執(zhí)行了await的之后的代碼,于是Task對(duì)象完成。主線程恢復(fù)執(zhí)行。(注意,即使“await之后沒有代碼”,即GetHtml()方法體中直接寫return await httpClient.GetStringAsync("/"),也是需要加.ConfigureAwait(false)的)

當(dāng)然,如果事件處理器是異步的,即使不加.ConfigureAwait(false),也不會(huì)有任何問題:

private async void Button_Click_8(object sender, RoutedEventArgs e)
    {
      string html = await GetHtml();

      txtLog.AppendText(html);
    }
    private async Task<string> GetHtml()
    {
      HttpClient httpClient = new HttpClient();
      httpClient.BaseAddress = new Uri("https://www.baidu.com/");
      string html = await httpClient.GetStringAsync("/");
      html = "【" + html + "】";
      return html;
    }

試想一下,如果GetHtml()被放到單獨(dú)的類中,做成類庫,那么,里面如果不加.ConfigureAwait(false),則只能假設(shè)使用這個(gè)類庫的人嚴(yán)格遵循異步編程規(guī)范了。一旦使用者在GetHtml()上執(zhí)行.Result,死鎖就無可避免了。

仔細(xì)看HttpClient的源代碼,可以發(fā)現(xiàn),它的GetStringAsync()方法也并不是“天生的”異步方法,它也是用await運(yùn)算符調(diào)用了自己的其他的異步方法,并且在每次調(diào)用后都添加了.ConfigureAwait(false)。

那么,最初的WPF程序的死鎖是否可以用.ConfigureAwait(false)解決呢?注意,txtLog是一個(gè)文本框,UI控件只能在UI線程訪問,所以添加上.ConfigureAwait(false)后會(huì)報(bào)錯(cuò):“InvalidOperationException: 調(diào)用線程無法訪問此對(duì)象,因?yàn)榱硪粋€(gè)線程擁有該對(duì)象”。那么是否可以改成這樣:

private void Button_Click_7(object sender, RoutedEventArgs e)
    {
      Method1().Wait();
    }

    private async Task Method1()
    {
      await Task.Delay(100).ConfigureAwait(false);

      Dispatcher.Invoke(() => {
        txtLog.AppendText("后續(xù)代碼");
      });
    }

依然是死鎖。所以,乖乖的用異步事件處理器吧:

private async void Button_Click_7(object sender, RoutedEventArgs e)
    {
      await Method1();
    }

    private async Task Method1()
    {
      await Task.Delay(100);

      txtLog.AppendText("后續(xù)代碼");
    }

上面的代碼還說明一個(gè)問題:在異步工具方法中,不要寫訪問UI控件的代碼,否則無法規(guī)避死鎖問題。

總結(jié)

  • 死鎖會(huì)發(fā)生在不遵循異步編程規(guī)范——在異步方法返回的Task對(duì)象上執(zhí)行Wait()或.Result時(shí)
  • ConfigureAwait(false)指定await后的代碼不返回原先的context,可以避免死鎖
  • 如果await之后的代碼不需要返回原先的context執(zhí)行,例如,僅僅是執(zhí)行Http請(qǐng)求,獲取和處理數(shù)據(jù),那么完全可以加上ConfigureAwait(false)。
  • 如果作為類庫的創(chuàng)作者,編寫異步方法時(shí),應(yīng)盡可能的使用ConfigureAwait(false),以保證一旦類庫的使用者阻塞異步方法時(shí),不會(huì)產(chǎn)生死鎖。
  • 在異步類庫/工具方法中,應(yīng)避免加入訪問UI控件的代碼 

附加  async/await學(xué)習(xí)資料

 C# Under the Hood: async/await  作者從動(dòng)手寫一個(gè)“可等待”的方法開始,進(jìn)而通過反編譯工具分析異步方法生成的的實(shí)質(zhì)代碼,揭示了async/await的本質(zhì)——回調(diào)

What happens in an async method  msdn編程指南,圖示異步方法的執(zhí)行流程

到此這篇關(guān)于淺談C# async await 死鎖問題總結(jié)的文章就介紹到這了,更多相關(guān)C# async await 死鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C# 遍歷文件夾子目錄下所有圖片及遍歷文件夾下的文件

    C# 遍歷文件夾子目錄下所有圖片及遍歷文件夾下的文件

    在上個(gè)項(xiàng)目開發(fā)中遇到這樣的需求,取指定目錄下面的所有圖片,以表格的型式展示并顯示該圖片的相對(duì)路徑。下面小編給大家分享C# 遍歷文件夾子目錄下所有圖片及遍歷文件夾下的文件,一起看看吧
    2017-01-01
  • C#最簡單的關(guān)閉子窗體更新父窗體的實(shí)現(xiàn)方法

    C#最簡單的關(guān)閉子窗體更新父窗體的實(shí)現(xiàn)方法

    原理就是將子窗體最為對(duì)話框模式彈出,當(dāng)窗體關(guān)閉或取消時(shí)更新主窗體
    2012-11-11
  • C# String字符串案例詳解

    C# String字符串案例詳解

    這篇文章主要介紹了C# String字符串案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • 詳解c# 多態(tài)

    詳解c# 多態(tài)

    這篇文章主要介紹了c# 多態(tài)的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • c#圖片上傳和顯示的實(shí)現(xiàn)方法

    c#圖片上傳和顯示的實(shí)現(xiàn)方法

    這篇文章主要介紹了c#實(shí)現(xiàn)圖片上傳和顯示的實(shí)現(xiàn)方法,可實(shí)現(xiàn)圖片上傳效果預(yù)覽功能,需要的朋友可以參考下。
    2016-10-10
  • 詳解C#如何利用爬蟲技術(shù)實(shí)現(xiàn)快捷租房

    詳解C#如何利用爬蟲技術(shù)實(shí)現(xiàn)快捷租房

    做為一個(gè)碼農(nóng),大部分都集中在一二線城市,所以租房也就無可避免,面對(duì)如今五花八門的租房信息,往往很難找到合適的房子。本文教你如何利用爬蟲技術(shù)實(shí)現(xiàn)快捷租房,感興趣的可以了解一下
    2022-09-09
  • 使用C#表達(dá)式樹實(shí)現(xiàn)對(duì)象的深克隆(實(shí)例詳解)

    使用C#表達(dá)式樹實(shí)現(xiàn)對(duì)象的深克隆(實(shí)例詳解)

    C# 的表達(dá)式樹提供了一個(gè)強(qiáng)大的機(jī)制,可以將代碼以數(shù)據(jù)結(jié)構(gòu)的形式表示出來,使得代碼可以在運(yùn)行時(shí)進(jìn)行檢查、修改或執(zhí)行,這為動(dòng)態(tài)查詢生成、代碼優(yōu)化和動(dòng)態(tài)編程提供了很多可能性,這篇文章主要介紹了使用C#強(qiáng)大的表達(dá)式樹實(shí)現(xiàn)對(duì)象的深克隆,需要的朋友可以參考下
    2024-05-05
  • C# DirectShow預(yù)覽攝像頭并截圖

    C# DirectShow預(yù)覽攝像頭并截圖

    這篇文章主要為大家詳細(xì)介紹了C# DirectShow預(yù)覽攝像頭并截圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • 綁定winform中DataGrid

    綁定winform中DataGrid

    綁定winform中DataGrid,需要的朋友可以參考一下
    2013-02-02
  • 大白話講解C# 中的委托

    大白話講解C# 中的委托

    這篇文章主要介紹了C# 中的委托的相關(guān)資料,幫助初學(xué)者更好的理解和使用c#,感興趣的朋友可以了解下
    2020-11-11

最新評(píng)論

汶上县| 河西区| 清涧县| 五华县| 高安市| 绥棱县| 前郭尔| 陇川县| 察隅县| 宜阳县| 当涂县| 敦化市| 普安县| 明光市| 兴义市| 丰台区| 资溪县| 囊谦县| 广丰县| 安西县| 长子县| 徐闻县| 根河市| 十堰市| 西充县| 棋牌| 揭阳市| 永靖县| 虎林市| 巫山县| 托里县| 馆陶县| 绥滨县| 鄢陵县| 长武县| 天峻县| 泸西县| 贵阳市| 武宣县| 苍山县| 嫩江县|