日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

.NET 异步,你也许不知道的5种用法

發布時間:2023/12/4 asp.net 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET 异步,你也许不知道的5种用法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

async/await異步操作,是C#中非常驚艷的“語法糖”,讓異步編程變得優美且傻瓜化到了不可思議的程度。就連JavaScript都借鑒了async/await語法,讓回調泛濫的JavaScript代碼變得很優美。

我之前錄制的.NET視頻教程已經把async/await等基礎知識介紹了,這篇文章不再介紹那些基礎知識,如果有對它們還不了解的朋友,請到我的B站、頭條、油管等平臺搜索“楊中科 .net 教程”查看。

本篇文章只對在之前的視頻教程中沒有提到的幾點做講解。

?

用法1、控制并行執行的任務數量

?????? 在項目開發的時候,有時候有很多任務需要異步執行,但是為了避免同時執行的異步任務太多,反而降低性能,因此通常需要限制并行執行的任務的數量。比如爬蟲并行從網上抓取內容的時候,就要根據情況限制最大執行的線程的數量。

在沒有async/await的年代,需要使用信號量等機制來進行線程間通訊來協調各個線程的執行,需要開發者對于多線程的技術細節非常了解。而使用async/await之后,這一切就可以變得非常傻瓜化了。

比如下面的代碼用來首先從words.txt這個每行一個英文單詞的字典中,逐個讀取單詞,然后調用一個API接口來獲得單詞的“音標、中文含義、例句”等詳細信息。為了加快處理速度,需要采用異步編程來實現多任務同時下載,但是又要限制同時執行的任務的數量(假設為5個)。實現代碼如下:

class Program {static async Task Main(string[] args){ServiceCollectionservices = new ServiceCollection();services.AddHttpClient();services.AddScoped<WordProcessor>();using(var sp = services.BuildServiceProvider()){var wp = sp.GetRequiredService<WordProcessor>();string[]words = await File.ReadAllLinesAsync("d:/temp/words.txt");List<Task>tasks = new List<Task>();foreach(var word in words){tasks.Add(wp.ProcessAsync(word));if(tasks.Count==5){//waitwhen five tasks are readyawai tTask.WhenAll(tasks);tasks.Clear();}}//waitthe remnant which are less than five.await Task.WhenAll(tasks);}Console.WriteLine("done!");} }class WordProcessor {private IHttpClientFactory httpClientFactory;public WordProcessor(IHttpClientFactory httpClientFactory){this.httpClientFactory= httpClientFactory;}publicasync Task ProcessAsync(string word){Console.WriteLine(word);var httpClient = this.httpClientFactory.CreateClient();string json = await httpClient.GetStringAsync("http://dict.e.opac.vip/dict.php?sw="+ Uri.EscapeDataString(word));await File.WriteAllTextAsync("d:/temp/words/" + word + ".txt",json);} }

?

核心代碼就是下面這一段:

List<Task> tasks = newList<Task>(); foreach(var word in words) {tasks.Add(wp.ProcessAsync(word));if(tasks.Count==5){//waitwhen five tasks are readyawait Task.WhenAll(tasks);tasks.Clear();} }

這里遍歷所有單詞,抓取單詞并且保存到磁盤的Process方法的返回值Task沒有使用await關鍵字進行修飾,而是把返回的Task對象保存到list中,由于沒有使用await進行等待,因此不用等一個任務執行完成,就可以把下一個任務加入list。當list中的任務滿五個的時候,就調用await Task.WhenAll(tasks);等待這五個任務執行完成后,再處理下一組(5個)。循環之外的await Task.WhenAll(tasks);的是用來處理最后一組不足5個任務的情況。

?

用法2、在BackgroundService等異步執行的代碼中進行DI注入

?

????依賴注入(DI)的時候,注入的對象都是有生命周期的。比如使用services.AddDbContext<TestDbContext>(...);這種方式注入EF Core中的DbContext的時候,TestDbContext的生命周期就是Scope。在普通的MVC的Controller中可以直接注入TestDbContext,但是在BackgroundService中是不能直接注入TestDbContext的。這時候,可以注入IServiceScopeFactory對象,然后在使用到TestDbContext對象的時候再調用IServiceScopeFactory的CreateScope()方法來生成一個IServiceScope,并且使用IServiceScope的ServiceProvider來手動解析獲取TestDbContext對象。

代碼如下:

public classTestBgService:BackgroundService {private readonly IServiceScopeFactory scopeFactory;public TestBgService(IServiceScopeFactory scopeFactory){this.scopeFactory= scopeFactory;}protected override Task ExecuteAsync(CancellationToken stoppingToken){using(var scope = scopeFactory.CreateScope()){var sp = scope.ServiceProvider;var dbCtx = sp.GetRequiredService<TestDbContext>();foreach(var b in dbCtx.Books){Console.WriteLine(b.Title);}} return Task.CompletedTask;} }

?

用法3、異步方法可以不await

我在做youzack背單詞的時候,有一個查詢單詞的功能。為了提升客戶端的響應速度,我把每個單詞的明細信息都按照“每個單詞一個json文件”的形式,把單詞的詳細信息保存到文件服務器,相當于做了一個“靜態化”。因此客戶端在查詢單詞的時候,先到文件服務器中查找一下是否有對應的靜態文件,如果有的話,就直接加載靜態文件。如果在文件服務器不存在的話,再調用API接口的方法去查詢,API接口從數據庫中查詢到單詞后,不僅會把單詞的詳細信息返回給客戶端,而且還會把單詞的詳細信息再上傳到文件服務器。這樣以后客戶端再查詢這個單詞,就可以直接從文件服務器查詢了。

因此API接口中“把從數據庫中查詢到的單詞的詳細信息上傳到文件服務器”這個操作對于接口的請求者來講沒什么意義,而且會降低接口的響應速度,因此我就把“上傳到文件服務器”這個操作寫到了異步方法中,并且沒有通過await來等待。

偽代碼如下:

public async Task<WordDetail>FindWord(string word) {var detail = await db.FindWordInDBAsync(word);//從數據庫里查詢_=storage.UploadAsync($”{word}.json”,detail.ToJsonString());//上傳到文件服務器,但是不等待returnd etail; }

?

在上面的UploadAsync調用中沒有await調用等待,因此只要從數據庫中查詢出來,就把detail返回給請求者了,留下UploadAsync在異步線程中慢慢執行。

?

前面加的“_=”是消除對于不await異步方法造成編譯器警告。

?

用法4、異步代碼中Sleep的坑

?

????在編寫代碼的時候,有時候我們需要“暫停一段時間,再繼續執行代碼”。比如調用一個Http接口,如果調用失敗,則需要等待2秒鐘再重試。

????在異步方法中,如果需要“暫停一段時間”,那么請使用Task.Delay(),而不是Thread.Sleep(),因為Thread.Sleep()會阻塞主線程,就達不到“使用異步提升系統并發能力”的目的了。

如下代碼是錯誤的:

public async Task<IActionResult> TestSleep() {await System.IO.File.ReadAllTextAsync("d:/temp/words.txt");Console.WriteLine("firstdone");Thread.Sleep(2000);awaitSystem.IO.File.ReadAllTextAsync("d:/temp/words.txt");Console.WriteLine("seconddone");returnContent("xxxxxx"); }

上面的代碼是能夠正確的編譯執行的,但是會大大降低系統的并發處理能力。因此要用Task.Delay()代替Thread.Sleep()。如下是正確的:

public async Task<IActionResult> TestSleep() {awaitSystem.IO.File.ReadAllTextAsync("d:/temp/words.txt");Console.WriteLine("firstdone");awaitTask.Delay(2000);//!!!awaitSystem.IO.File.ReadAllTextAsync("d:/temp/words.txt");Console.WriteLine("seconddone");returnContent("xxxxxx"); }

?

用法5、yield如何用到異步方法中

??? yield由于可以實現“產生一個數據就讓IEnumerable的使用者處理一個數據”,從而實現數據處理的“流水線化”,提升數據處理的速度。

????但是,由于yield和async都是編譯器提供的語法糖,編譯器都會把它們修飾的方法編譯為一個使用了狀態機的類。因此兩個語法糖碰到一起,編譯器就迷惑了,因此不能直接在async修飾的異步方法中使用yield返回數據。

因此下面的代碼是錯誤的:

static async IEnumerable<int>ReadCC() {foreach(string line in await File.ReadAllLinesAsync("d:/temp/words.txt")){yieldreturn line.Length;} }

只要把IEnumerable改成IAsyncEnumerable就可以了,如下是正確的:

static async IAsyncEnumerable<int>ReadCC() {foreach(stringline in await File.ReadAllLinesAsync("d:/temp/words.txt")){yieldreturn line.Length;} }

但是調用同時使用了async和yield的代碼,不能使用普通的foreach+await,如下是錯誤的:

foreach (int i in await ReadCC()) {Console.WriteLine(i); }

?

需要把await關鍵詞移動到在foreach之前,如下是正確的:

await foreach(int i in ReadCC()) {Console.WriteLine(i); }

編譯器是微軟寫的,不知道為什么不支持foreach (int i in awaitReadCC())這樣的寫法,可能是由于為了兼容之前的C#語法規范不得已而為之吧。

總結

以上是生活随笔為你收集整理的.NET 异步,你也许不知道的5种用法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。