怎么查看网站用的php还是.net,三星网上商城发货速度,网站建设最重要的因素,东莞软件设计一文看懂async和“await”关键词是如何简化了C#中多线程的开发过程当我们使用需要长时间运行的方法#xff08;即#xff0c;用于读取大文件或从网络下载大量资源#xff09;时#xff0c;在同步的应用程序中#xff0c;应用程序本身将停止运行#xff0c;直… 一文看懂async和“await”关键词是如何简化了C#中多线程的开发过程当我们使用需要长时间运行的方法即用于读取大文件或从网络下载大量资源时在同步的应用程序中应用程序本身将停止运行直到活动完成。在这些情况下异步编程非常有用它使我们能够并行执行不同任务并在需要时等待其完成。这种方法有许多不同的模型类型APM异步编程模型基于事件异步模型EAP以及TAP基于任务的异步模型任务。让我们看看如何使用关键字async和await在C中实现第三个方法。编写异步代码的主要问题之一是可维护性实际上许多人普遍认为这种编程方法会使代码复杂化。幸运的是C5引入了一种简化的方法在该方法中编译器运行由开发人员先前完成的艰巨任务并且应用程序保留类似于同步代码的逻辑结构。让我们举个例子。假设我们有一个.NET Core项目我们应该在其中管理三个实体AreaCompany和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; }
}
现在假设我们应该使用Entity Framework Core将这些实体的值保存在数据库中。其DbContext是public class AppDbContext : DbContext
{public DbSetArea Areas { get; set; }public DbSetCompany Companies { get; set; }public DbSetResource Resources { get; set; }public AppDbContext(DbContextOptionsAppDbContext options) : base(options) {}override protected void OnModelCreating(ModelBuilder modelBuilder){modelBuilder.EntityArea ().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.EntityCompany ().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.EntityResource().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});}
}
从代码中可以看到我们插入了一些示例数据进行处理。现在假设我们要使用Controller API公开这些数据既单独针对每个实体又使用将它们全部联接在一起的方法并通过一次调用返回它们。使用同步方法Controller API 将是[ApiController]
[Route([controller])]
public class DataController : ControllerBase
{private readonly AppDbContext db null;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方法在其中调用返回单个结果的三个方法并等待每个方法的执行完成后再传递到下一个结果。这三种方法互不相关因此您无需等待其中一种方法的执行即可调用另一种方法。然后您可以创建三个独立的任务以并行执行。第一种方法可以基于该方法Task.Run()作业运行在线程池之上并返回一个任务对象它代表了这项工作。这样方法可以在线程池的不同线程上同时运行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 });
}
Task的Result属性包含详细说明的结果。方法WhenAll允许暂停当前线程执行直到所有Task完成。运行代码我们可以注意到一个有趣的事情调用中断并启动以下异常AggregateException发生一个或多个错误。在上一个操作完成之前第二个操作在此上下文上开始。这通常是由使用相同DbContext实例的不同线程引起的。有关如何避免DbContext线程问题的更多信息请参见https://go.microsoft.com/fwlink/?linkid2097913。[1]此错误消息告诉我们方法在不同的线程上同时执行但是由于它们使用与DbContext 相同的实例来连接数据库 因此引发了异常DbContext类无法确保线程安全的功能我们可以轻松地绕过此问题避免了.NET Core 的依赖注入引擎创建单个实例而我们为每种方法创建了单独的实例。作为示例让我们看看方法GetAreas会如何变化public class DataController : ControllerBase
{private readonly DbContextOptionsBuilder AppDbContext optionsBuilder null;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();}}
}
好吧现在可以了。我们应该注意EFCore提供了一些方法例如与方法ToArrayAsync一样使用相同的DbContext进行异步调用该方法从IQueryable 创建一个数组该数组 异步枚举它。此方法返回Task 它是表示异步操作的活动。这样我们不再需要使用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 TaskArea[] GetAreas()
{return db.Areas.ToArrayAsync();
}
无论如何Microsoft不能保证这些异步方法在每种情况下都能工作因为DbContext尚未设计为线程安全的。您可以查询此链接以获取更多信息https : //docs.microsoft.com/zh-cn/ef/core/querying/async使用Entity Framework Core时最佳实践是在启动另一个异步操作之前为每个异步操作都拥有一个DbContext或等待每个异步操作完成。当我们必须进行异步调用并返回结果时这种最佳做法是可以的。但是如果我们想在返回结果之前对结果进行一些操作会发生什么如果我们想向列表中添加元素怎么办我们应该等待结果添加元素然后返回修改后的列表[Route(companies)]
public TaskCompany[] 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();}
}
不幸的是该代码无法编译因为data.ToArray返回的是数组而不是Task。实际上这里我们需要三个线程主调用方Get数据库查询this.db.Companies.ToListAsync和一个线程该线程将一个值添加到列表中。我们有三种方法可以做到这一点让我们用三种单一方法来查看它们。我们已经看到的第一个可以使用Task.Run方法[Route(companies)]
public TaskCompany[] 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该方法可以应用于任务并且可以在上一个方法完成后立即指定要运行的新任务[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();});}
}
我们可以让编译器执行“垃圾代码”并使用关键字async和await这可以为我们创建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();}
}
正如您在最后一种方法中看到的那样代码更加简单并且向我们隐藏了Task的创建从而使我们可以异步返回。让我们想象一下一个场景其中调用不止一个并且这种方法如何使一切变得更加线性。重构的作用是方法GetAreas已成为异步操作。这个事实意味着当不同的请求到达此API时分配给该请求的线程池的线程将被释放以供其他请求使用直到DbContext终止数据提取为止。我希望我能引起您足够的兴趣来深入分析该论点。在许多情况下使用async和await非常方便并且除了使代码更加简洁和线性外还可以提高一般应用程序的性能。示例代码见https//github.com/fvastarella/Programmazione-asincrona-con-async-awaitReferences[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