在 c# 中何时需要异步和等待
本文关键字:异步 等待 何时需 | 更新日期: 2023-09-27 18:31:13
正如标题所说。 我想知道我是否正在异步编写并在不需要时等待。
我见过这样的带有异步标签的方法
public async Task CreateAsync(User user)
{
if (_context.Entry<User>(user).State == EntityState.Detached)
{
_context.Set<User>().Add(user);
}
_context.Entry<User>(user).State = EntityState.Added;
await _context.SaveChangesAsync();
}
就像这样没有它
public Task CreateAsync(User user)
{
if (_context.Entry<User>(user).State == EntityState.Detached)
{
_context.Set<User>().Add(user);
}
_context.Entry<User>(user).State = EntityState.Added;
return _context.SaveChangesAsync();
}
两者都编译良好。 我总是添加异步和等待关键字,想知道我是否做错了并在不需要它们时编写它们?
编辑:
如果您实际上返回了一个值,则应使用 async/await 关键字编写该值,还是不使用。这是一个带有关键字的版本
public async Task<User> CreateAsync(User user)
{
if (_context.Entry<User>(user).State == EntityState.Detached)
{
_context.Set<User>().Add(user);
}
_context.Entry<User>(user).State = EntityState.Added;
await _context.SaveChangesAsync();
return user;
}
这是另一个例子
public Task<User> FindByIdAsync(long userId)
{
return _context.Users.FindAsync(userId);
}
public async Task<User> FindByIdAsync(long userId)
{
return await _context.Users.FindAsync(userId);
}
编辑 2
到目前为止,答案很好,但最后一个例子是 1 个。 由于我有异步调用,我将如何应对从 1 个方法调用多个异步函数。 这是我拥有的一个例子,但我不知道它是否正确。 我不希望该方法在所有 AddAsync 方法完成之前退出。这是正确的吗
private async Task AddPermissions(DataContext context)
{
var permissionService = new PermissionService(context);
await permissionService.AddAsync(new Permission("CanView", "View company"));
await permissionService.AddAsync(new Permission("CanAdd", "Add and view company"));
await permissionService.AddAsync(new Permission("CanEdit", "Edit and view company"));
await permissionService.AddAsync(new Permission("CanDelete", "Delete and view company record"));
await permissionService.AddAsync(new Permission("CanAdd", "Add new pages"));
await permissionService.AddAsync(new Permission("CanEdite", "Edit existing pages"));
await permissionService.AddAsync(new Permission("CanDelete", "Delete a page"));
await permissionService.AddAsync(new Permission("CanAdd", "Add new page content"));
await permissionService.AddAsync(new Permission("CanEdit", "Edit existing page content"));
await permissionService.AddAsync(new Permission("CanDelete", "Delete page content"));
}
我直言,如果您希望SaveChangesAsync
操作在方法返回时完成,或者您需要对异步操作的结果执行某些操作,则只需要使用该await
。在这种情况下,您不会对它执行任何操作,因此最好不要使用异步方法,并避免使用该方法生成状态机,从而生成更高效的代码。
关于你的第二次编辑,你是对的。尽管该方法将在遇到第一个await
后立即返回,但所有其他等待的语句将在线程池上一个接一个地执行,然后更新任务的结果。因此,如果您await AddPermissions
该语句将仅在所有内部permissionService.AddAsync
调用完成后完成,除非引发异常。
如有必要,还可以并行执行permissionService.AddAsync
调用,方法是将返回的任务存储在列表中,然后等待Task.WhenAll
private async Task AddPermissions(DataContext context)
{
var permissionService = new PermissionService(context);
List<Task> permissionRequests = new List<Task>();
permissionRequests.Add(permissionService.AddAsync(new Permission("CanView", "View company")));
permissionRequests.Add(permissionService.AddAsync(new Permission("CanAdd", "Add and view company")));
permissionRequests.Add(permissionService.AddAsync(new Permission("CanEdit", "Edit and view company")));
permissionRequests.Add(permissionService.AddAsync(new Permission("CanDelete", "Delete and view company record")));
permissionRequests.Add(permissionService.AddAsync(new Permission("CanAdd", "Add new pages")));
permissionRequests.Add(permissionService.AddAsync(new Permission("CanEdite", "Edit existing pages")));
permissionRequests.Add(permissionService.AddAsync(new Permission("CanDelete", "Delete a page")));
permissionRequests.Add(permissionService.AddAsync(new Permission("CanAdd", "Add new page content")));
permissionRequests.Add(permissionService.AddAsync(new Permission("CanEdit", "Edit existing page content")));
permissionRequests.Add(permissionService.AddAsync(new Permission("CanDelete", "Delete page content")));
await Task.WhenAll(permissionRequests);
}
因此,每次调用permissionService.AddAsync
都会启动请求并将相应的任务添加到列表中。启动所有请求后,您可以使用 await Task.WhenAll
await
完成所有请求,这将等到它们全部完成或返回错误。引发的任何异常都将存储在从 Task.WhenAll
返回的任务中。等待该任务将重新引发第一个异常,但您可以使用包含 Task.Excpetion
属性访问所有这些异常,该属性包含一个 AggregatedException
,这反过来又包含所有引发的异常。
第二个版本效率稍高,但它不允许你稍后返回并添加更多代码,而无需重写它以使用 async/await。
差不多就是这样。如果我在我的代码库中看到任何一种模式,我都不会抱怨。
这个话题可能很深入,但这里有一个高层次的概述。
您上面发布的两个版本的代码之间存在关键区别。但首先是重要的相似性。
它们都告诉_context
异步保存更改。因为这就是SaveChangesAsync
的实施方式。
现在,区别..
在第一个版本中,当您使用 async
和 await
关键字时,任何可能在await
调用之后的代码(在这种情况下,await
调用是函数中的最后一次调用),编译器会将其转换为延续,并且应该在await
调用完成后执行(异步)。这包括可能包装异步调用的任何异常处理。
相反,在第二个版本中,当我们把异步调用的返回值作为Task
时,当异步操作已经启动并且尚未完成时,执行将在该方法中继续(它不会被编译器变成延续)。没有代码设置为在异步操作完成后执行(除非在Task
对象上显式使用 .ContinueWith
)。
为什么要使用一个而不是另一个?
同样,在高级别async
和await
对于正常情况应该很好,在这些情况下,您希望利用编译器为您做一些魔术的优势,以便更轻松地处理异步调用。但是,对于某些方案,它的灵活性也较低。示例 - 如果要异步启动 10 个 ping 操作,然后在所有 10 个操作完成后写入延续,该怎么办?对于每个异步 ping 使用 async
和 await
关键字是不可能的。(第一次调用 await 会使代码的其余部分成为第一次异步调用的延续)。
在互联网上搜索更多有关async
await
的详细信息..它可能非常深入,但值得了解细节。
方法是调用 Task.Factory.StartNew
Task.Factory.StartNew(() => new Permission("CanView", "View company"));
Task.Factory.StartNew(() => new Permission("CanAdd", "Add and view company"));
...