避免在C#循環(huán)中使用await的方法小結(jié)
引言
在C#中,異步編程因其能夠提升應(yīng)用程序性能和響應(yīng)能力而變得越來越流行。async和await關(guān)鍵字使得編寫異步代碼變得更加容易,但如果使用不當(dāng),它們也可能引入一些陷阱。一個(gè)常見的錯(cuò)誤是在循環(huán)中使用await,這可能導(dǎo)致性能瓶頸和意外行為。在本文中,我們將探討為什么應(yīng)該避免在C#循環(huán)中使用await,并討論一些更高效地處理異步操作的替代方法。
一 在循環(huán)中使用await的問題
1、順序執(zhí)行
當(dāng)在循環(huán)中使用await時(shí),每次迭代都會等待前一次迭代完成后再開始。這導(dǎo)致了順序執(zhí)行,抵消了異步編程的好處。請看以下示例:
foreach (var item in items) { await ProcessItemAsync(item); }
在這段代碼中,每次迭代都會等待ProcessItemAsync完成后再進(jìn)行下一次迭代。如果ProcessItemAsync需要較長時(shí)間才能完成,這會導(dǎo)致性能不佳。
示例場景
假設(shè)我們需要通過異步下載處理一組URL的內(nèi)容。在循環(huán)中使用await的代碼如下:
foreach (var url in urls) { var content = await DownloadContentAsync(url); ProcessContent(content); }
在這種情況下,每個(gè)URL都是一個(gè)接一個(gè)地處理,導(dǎo)致總執(zhí)行時(shí)間是所有單個(gè)下載時(shí)間的總和。如果我們有10個(gè)URL,每個(gè)下載需要1秒,總執(zhí)行時(shí)間將大約是10秒。
2、資源爭用
在循環(huán)中使用await還可能導(dǎo)致資源爭用。每次迭代都會占用資源(如內(nèi)存和網(wǎng)絡(luò)連接)直到等待的任務(wù)完成。這可能導(dǎo)致可用資源的耗盡,尤其是在處理大量任務(wù)時(shí)。
二 更好的替代方法
1、使用Task.WhenAll
為了并發(fā)執(zhí)行異步操作,我們可以使用Task.WhenAll
。這種方法允許我們一次啟動所有異步任務(wù),并等待它們?nèi)客瓿伞R韵率侨绾沃貙懬懊娴氖纠?/p>
using System.Diagnostics; using System.Net; namespace ConsoleApp1 { public class Program { public async static Task Main(string[] args) { var urls = new List<string> { "https://www.163.com", "https://www.microsoft.com", "https://www.baidu.com" }; Stopwatch sw = Stopwatch.StartNew(); foreach (var url in urls) { var content = await DownloadContentAsync(url); ProcessContent(content); } Console.WriteLine($"foreach總共用時(shí):{sw.Elapsed.TotalSeconds}秒"); Stopwatch sw2 = Stopwatch.StartNew(); var downloadTasks = urls.Select(url => DownloadContentAsync(url)).ToArray(); var contents = await Task.WhenAll(downloadTasks); foreach (var content in contents) { ProcessContent(content); } Console.WriteLine($"WhenAll總共用時(shí):{sw2.Elapsed.TotalSeconds}秒"); } public async static Task<string> DownloadContentAsync(string url) { using (var client = new HttpClient(new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.GZip })) { client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"); var response = await client.GetAsync(url); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); } } public static void ProcessContent(string content) { Console.WriteLine($"處理內(nèi)容的長度: {content.Length}");// 這里可以添加更多的內(nèi)容處理邏輯 } } } }
在這個(gè)版本中,所有下載任務(wù)同時(shí)啟動,我們等待它們?nèi)客瓿珊笤偬幚斫Y(jié)果。這種方法顯著減少了總執(zhí)行時(shí)間,因?yàn)槿蝿?wù)是并行運(yùn)行的。經(jīng)測驗(yàn),循環(huán)多少次,Task.WhenAll的速度比foreach的速度快大概多少倍!
2、使用Parallel.ForEachAsync
C#還提供了Parallel.ForEachAsync,它允許你在不阻塞主線程的情況下并行運(yùn)行異步操作:
await Parallel.ForEachAsync(urls, async (url, cancellationToken) => { var content = await DownloadContentAsync(url); ProcessContent(content); });
Parallel.ForEachAsync確保多個(gè)迭代可以并發(fā)運(yùn)行,提升性能的同時(shí)保持代碼的簡潔和可讀性。
Parallel.ForEachAsync的運(yùn)行效率與Task.WhenAll的效率差不多
3、限制并發(fā)
在某些情況下,運(yùn)行過多的并發(fā)任務(wù)可能會使系統(tǒng)資源不堪重負(fù)。我們可以通過使用SemaphoreSlim來限制并發(fā)級別:
這個(gè)在執(zhí)行過程中產(chǎn)生了異常,等解決了在繼續(xù)討論
這種方法限制了并發(fā)任務(wù)的數(shù)量,更有效地管理資源使用。
三 結(jié)論
雖然await是C#異步編程的強(qiáng)大工具,但在循環(huán)中使用它可能導(dǎo)致性能不佳和資源爭用。通過理解順序執(zhí)行的影響,并利用Task.WhenAll、Parallel.ForEachAsync和SemaphoreSlim等替代方法,我們可以編寫更高效和健壯的異步代碼。避免在循環(huán)中使用await并采用更好的模式將提升你的應(yīng)用程序性能,使代碼更易維護(hù)和擴(kuò)展。遵循這些最佳實(shí)踐,你可以充分利用C#異步編程的潛力。
以上就是避免在C#循環(huán)中使用await的方法小結(jié)的詳細(xì)內(nèi)容,更多關(guān)于避免C#循環(huán)使用await的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#實(shí)現(xiàn)給定字符串生成MD5哈希的方法
這篇文章主要介紹了C#實(shí)現(xiàn)給定字符串生成MD5哈希的方法,涉及C#操作字符串的相關(guān)技巧,需要的朋友可以參考下2015-06-06C#中的WebRequest與WebResponse抽象類、DNS靜態(tài)類、Ping類介紹
這篇文章介紹了C#中的WebRequest與WebResponse抽象類、DNS靜態(tài)類、Ping類,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05C#使用Exchange實(shí)現(xiàn)發(fā)送郵件
最近項(xiàng)目中需要用到exchange的操作,所以本文就參照msdn弄了一個(gè)簡單的C#操作類,實(shí)現(xiàn)了發(fā)送郵件和拉取收件箱的功能,感興趣的小伙伴可以了解下2023-10-10C#實(shí)現(xiàn)功能強(qiáng)大的中國農(nóng)歷日歷操作類
這篇文章主要介紹了C#實(shí)現(xiàn)功能強(qiáng)大的中國農(nóng)歷日歷操作類,實(shí)例分析了C#操作時(shí)間及字符串的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-03-03C#進(jìn)行PDF表單域的創(chuàng)建填寫與刪除操作
通常情況下,PDF文件是不可編輯的,但PDF表單提供了一些可編輯區(qū)域,允許用戶填寫和提交信息,本文主要介紹了如何使用C#實(shí)現(xiàn)PDF表單域的創(chuàng)建,填寫與刪除操作,感興趣的可以了解下2024-04-04