亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢(xún)客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

一文看懂"async"和“await”關(guān)鍵詞是如何簡(jiǎn)化了C#中多線程的開(kāi)發(fā)過(guò)程

當(dāng)我們使用需要長(zhǎng)時(shí)間運(yùn)行的方法(即,用于讀取大文件或從網(wǎng)絡(luò)下載大量資源)時(shí),在同步的應(yīng)用程序中,應(yīng)用程序本身將停止運(yùn)行,直到活動(dòng)完成。在這些情況下,異步編程非常有用:它使我們能夠并行執(zhí)行不同任務(wù),并在需要時(shí)等待其完成。

這種方法有許多不同的模型類(lèi)型:APM(異步編程模型),基于事件(異步模型EAP),以及TAP,基于任務(wù)的(異步模型任務(wù))。讓我們看看如何使用關(guān)鍵字async和await在C#中實(shí)現(xiàn)第三個(gè)方法。

編寫(xiě)異步代碼的主要問(wèn)題之一是可維護(hù)性:實(shí)際上,許多人普遍認(rèn)為這種編程方法會(huì)使代碼復(fù)雜化。幸運(yùn)的是,C#5引入了一種簡(jiǎn)化的方法,在該方法中,編譯器運(yùn)行由開(kāi)發(fā)人員先前完成的艱巨任務(wù),并且應(yīng)用程序保留類(lèi)似于同步代碼的邏輯結(jié)構(gòu)。

讓我們舉個(gè)例子。假設(shè)我們有一個(gè).NET Core項(xiàng)目,我們應(yīng)該在其中管理三個(gè)實(shí)體:Area,Company和Resource。

public class Area{ public int Id { get; set; }
 [Required] [StringLength(255)] public string Name { get; set; }}
public class Company{ public int Id { get; set; }
 [Required] [StringLength(255)] public string Name { get; set; }}
public class Resource{ public int Id { get; set; }
 [Required] [StringLength(255)] public string Name { get; set; }}

現(xiàn)在假設(shè)我們應(yīng)該使用Entity Framework Core將這些實(shí)體的值保存在數(shù)據(jù)庫(kù)中。其DbContext是:

public class AppDbContext : DbContext{ public DbSet<Area> Areas { get; set; } public DbSet<Company> Companies { get; set; } public DbSet<Resource> Resources { get; set; } public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {}
 override protected void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Area> .HasData( new Area { Id = 1, Name = "Area1"}, new Area { Id = 2, Name = "Area2"}, new Area { Id = 3, Name = "Area3"}, new Area { Id = 4, Name = "Area4"}, new Area { Id = 5, Name = "Area5"}); modelBuilder.Entity<Company> .HasData( new Area { Id = 1, Name = "Company1"}, new Area { Id = 2, Name = "Company2"}, new Area { Id = 3, Name = "Company3"}, new Area { Id = 4, Name = "Company4"}, new Area { Id = 5, Name = "Company5"}); modelBuilder.Entity<Resource>.HasData( new Area { Id = 1, Name = "Resource1"}, new Area { Id = 2, Name = "Resource2"}, new Area { Id = 3, Name = "Resource3"}, new Area { Id = 4, Name = "Resource4"}, new Area { Id = 5, Name = "Resource5"}); }}

從代碼中可以看到,我們插入了一些示例數(shù)據(jù)進(jìn)行處理。現(xiàn)在假設(shè)我們要使用Controller API公開(kāi)這些數(shù)據(jù),既單獨(dú)(針對(duì)每個(gè)實(shí)體),又使用將它們?nèi)柯?lián)接在一起的方法,并通過(guò)一次調(diào)用返回它們。

使用同步方法,Controller API 將是:

[ApiController][Route("[controller]")]public class DataController : ControllerBase{ private readonly AppDbContext db = ;
 public DataController(AppDbContext db) { this.db = db; }
 public IActionResult Get { var areas = this.GetAreas; var companies = this.GetCompanies; var resources = this.GetResources; return Ok(new { areas = areas, companies = companies, resources = resources }); }
 [Route("areas")] public Area GetAreas  { return this.db.Areas.ToArray; }
 [Route("companies")] public Company GetCompanies  { return this.db.Companies.ToArray; }
 [Route("resources")] public Resource GetResources  { return this.db.Resources.ToArray; }}

Get方法在其中調(diào)用返回單個(gè)結(jié)果的三個(gè)方法,并等待每個(gè)方法的執(zhí)行完成后再傳遞到下一個(gè)結(jié)果。這三種方法互不相關(guān),因此您無(wú)需等待其中一種方法的執(zhí)行即可調(diào)用另一種方法。然后,您可以創(chuàng)建三個(gè)獨(dú)立的任務(wù)以并行執(zhí)行。

第一種方法可以基于該方法Task.Run作業(yè)運(yùn)行在線程池之上,并返回一個(gè)任務(wù)對(duì)象,它代表了這項(xiàng)工作。這樣,方法可以在線程池的不同線程上同時(shí)運(yùn)行:

public IActionResult Get{ var areas = Task.Run( = > this.GetAreas); var companies = Task.Run( = > this.GetCompanies); var resources = Task.Run( = > this.GetResources);  Task.WhenAll(areas, companies, resources); return Ok(new { areas = areas.Result, companies = companies.Result, resources = resources.Result });}

TaskResult屬性包含詳細(xì)說(shuō)明的結(jié)果。方法WhenAll允許暫停當(dāng)前線程執(zhí)行,直到所有Task完成。運(yùn)行代碼,我們可以注意到一個(gè)有趣的事情:調(diào)用中斷,并啟動(dòng)以下異常:

AggregateException:發(fā)生一個(gè)或多個(gè)錯(cuò)誤。(在上一個(gè)操作完成之前,第二個(gè)操作在此上下文上開(kāi)始。這通常是由使用相同DbContext實(shí)例的不同線程引起的。有關(guān)如何避免DbContext線程問(wèn)題的更多信息,請(qǐng)參見(jiàn)
https://go.microsoft.com/fwlink/?linkid=2097913。[1]

此錯(cuò)誤消息告訴我們,方法在不同的線程上同時(shí)執(zhí)行,但是由于它們使用與DbContext相同的實(shí)例來(lái)連接數(shù)據(jù)庫(kù), 因此引發(fā)了異常,DbContext類(lèi)無(wú)法確保線程安全的功能:我們可以輕松地繞過(guò)此問(wèn)題,避免了.NET Core 的依賴(lài)注入引擎創(chuàng)建單個(gè)實(shí)例,而我們?yōu)槊糠N方法創(chuàng)建了單獨(dú)的實(shí)例。作為示例,讓我們看看方法GetAreas會(huì)如何變化:

public class DataController : ControllerBase{ private readonly DbContextOptionsBuilder <AppDbContext> optionsBuilder = ;
 public DataController(IConfiguration configuration) { this.optionsBuilder = new DbContextOptionsBuilder <AppDbContext>  .UseSqlite(configuration.GetConnectionString("DefaultConnection")); }
 [Route("areas")] public Area GetAreas  { using(var db = new AppDbContext(this.optionsBuilder.Options)) { return db.Areas.ToArray; } }}

好吧,現(xiàn)在可以了。我們應(yīng)該注意,EFCore提供了一些方法,例如,與方法ToArrayAsync一樣,使用相同的DbContext進(jìn)行異步調(diào)用,該方法從IQueryable 創(chuàng)建一個(gè)數(shù)組,該數(shù)組 異步枚舉它。此方法返回Task ,它是表示異步操作的活動(dòng)。

這樣,我們不再需要使用Task.Run:

public IActionResult Get{ var areas = this.GetAreas; var companies = this.GetCompanies; var resources = this.GetResources; Task.WhenAll(areas, companies, resources); return Ok(new { areas = areas.Result, companies = companies.Result, resources = resources.Result });}
[Route("areas")]public Task<Area> GetAreas { return db.Areas.ToArrayAsync;}

無(wú)論如何,Microsoft不能保證這些異步方法在每種情況下都能工作,因?yàn)镈bContext尚未設(shè)計(jì)為線程安全的。您可以查詢(xún)此鏈接以獲取更多信息:https :
//docs.microsoft.com/zh-cn/ef/core/querying/async

使用Entity Framework Core時(shí),最佳實(shí)踐是在啟動(dòng)另一個(gè)異步操作之前,為每個(gè)異步操作都擁有一個(gè)DbContext或等待每個(gè)異步操作完成。當(dāng)我們必須進(jìn)行異步調(diào)用并返回結(jié)果時(shí),這種最佳做法是可以的。

但是,如果我們想在返回結(jié)果之前對(duì)結(jié)果進(jìn)行一些操作,會(huì)發(fā)生什么?如果我們想向列表中添加元素怎么辦?我們應(yīng)該等待結(jié)果,添加元素,然后返回修改后的列表:

[Route("companies")]public Task<Company> GetCompanies { using (var db = new AppDbContext(this.optionsBuilder.Options)) { var data = this.db.Companies.ToListAsync.Result; data.Insert(0, new Company { Id = 0, Name = "-"}); return data.ToArray; }}

不幸的是,該代碼無(wú)法編譯,因?yàn)閐ata.ToArray返回的是數(shù)組而不是Task。實(shí)際上,這里我們需要三個(gè)線程:主調(diào)用方(Get),數(shù)據(jù)庫(kù)查詢(xún)(
this.db.Companies.ToListAsync)和一個(gè)線程,該線程將一個(gè)值添加到列表中。我們有三種方法可以做到這一點(diǎn):讓我們用三種單一方法來(lái)查看它們。我們已經(jīng)看到的第一個(gè),可以使用Task.Run方法:

[Route("companies")]public Task<Company> GetCompanies{ return Task.Run( => { using (var db = new AppDbContext(this.optionsBuilder.Options)) { var data = db.Companies.ToList; data.Insert(0, new Company { Id = 0, Name = "-" }); return data.ToArray; } });}

作為替代方案,我們可以使用方法ContinueWith,該方法可以應(yīng)用于任務(wù),并且可以在上一個(gè)方法完成后立即指定要運(yùn)行的新任務(wù):

[Route("resources")]public Task <Resource> GetResources{ using (var db = new AppDbContext(this.optionsBuilder.Options)) { return db.Resources.ToListAsync .ContinueWith(dataTask = > { var data = dataTask.Result; dataTask.Result.Insert(0, new Resource { Id = 0, Name = "-" }); return data.ToArray; }); }}

我們可以讓編譯器執(zhí)行“垃圾代碼”,并使用關(guān)鍵字asyncawait,這可以為我們創(chuàng)建Task:

[Route("areas")]public async Task <Area> GetAreas{ using (var db = new AppDbContext(this.optionsBuilder.Options)) { var data = await db.Areas.ToListAsync; data.Insert(0, new Area { Id = 0, Name = "-" }); return data.ToArray; }}

正如您在最后一種方法中看到的那樣,代碼更加簡(jiǎn)單,并且向我們隱藏了Task的創(chuàng)建,從而使我們可以異步返回。讓我們想象一下一個(gè)場(chǎng)景,其中調(diào)用不止一個(gè),并且這種方法如何使一切變得更加線性。

重構(gòu)的作用是方法GetAreas已成為異步操作。這個(gè)事實(shí)意味著,當(dāng)不同的請(qǐng)求到達(dá)此API時(shí),分配給該請(qǐng)求的線程池的線程將被釋放以供其他請(qǐng)求使用,直到DbContext終止數(shù)據(jù)提取為止。

我希望我能引起您足夠的興趣來(lái)深入分析該論點(diǎn)。在許多情況下,使用async和await非常方便,并且除了使代碼更加簡(jiǎn)潔和線性外,還可以提高一般應(yīng)用程序的性能。

示例代碼見(jiàn):

https://github.com/fvastarella/Programmazione-asincrona-con-async-await

 

References

[1]https:https://docs.microsoft.com/en-us/ef/core/querying/async

[2]//docs.microsoft.com/zh-cn/ef/core/querying/async:https://docs.microsoft.com/en-us/ef/core/querying/async

分享到:
標(biāo)簽:async
用戶(hù)無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定