错误:异步模块或处理程序已完成,而异步操作仍处于挂起状态
本文关键字:异步操作 挂起状态 已完成 异步 模块 程序 处理 错误 | 更新日期: 2023-09-27 18:10:25
我有一个控制器操作方法来保存用户详细信息,如下所示。
public async Task<ActionResult> SaveUser(ViewModel.VM_CreateUser user)
{
var result = await _user.Save(userDetails);
return Json(new { Success = String.IsNullOrEmpty(result) });
}
到目前为止,上述4行函数没有任何问题。
public async Task<ActionResult> SaveUser(ViewModel.VM_CreateUser user)
{
var result = await _user.Save(userDetails);
new MailController().CreateUser(user.userDetails); //<==This is problem line of code.
}
下面是我的邮件管理员。
public class MailController : MailerBase
{
public void CreateUser(ViewModel.VM_User user)
{
To.Add(user.EmailAddress);
From = System.Configuration.ConfigurationManager.AppSettings["emailSender"];
Subject = "Hi";
Email("CreateUser", user).DeliverAsync();
}
}
在上面的代码中,我在执行电子邮件发送代码时遇到了问题。我收到下面的错误消息。
异步模块或处理程序已完成,而异步操作仍在等待
请建议纠正措施!!
这是因为async
方法跟踪它们的完成,甚至是async void
。它们通过在启动时向SynchronizationContext
注册并在返回时标记操作完成来完成此操作。ASP.NET跟踪所有创建的操作,并要求在返回操作之前完成所有操作,否则会向HTTP客户端返回错误。如果需要运行"fire-and-forget"方法,则必须手动避免在ASP.NET SynchronizationContext
上启动该方法。例如,await Task.Run(() => CallFireAndForget())
(await
,这样您就可以等待同步部分在线程池上运行。这很有效,因为CallFireAndForget()
是async void
。要激发一个将Task
返回为即发即弃的方法,您必须明确避免将Task
返回给Task.Run()
,如下所示:await Task.Run(() => { CallFireAndForgetAsync(); })
(。
另一种显示相同错误消息的方法应该是在异步操作中写入:
// BAD CODE, DEMONSTRATION ONLY!
AsyncOperationManager.CreateOperation();
如果使用AsyncOperation
API,则必须确保在允许操作方法返回之前调用AsyncOperation.OperationCompleted()
。
在您的情况下,如果邮件发送真的是一项即发即弃任务,那么在将其签名更改为async Task CreateUserAsync(ViewModel.VM_User user)
后,您可以在CreateUser
内部执行以下操作:
await Task.Run(() => Email("CreateUser", user).DeliverAsync());
在你的行动中:
await new MailController().CreateUserAsync(user.userDetails);
理想情况下,您不会使用Task.Run()
进行类似的操作。相反,您可以临时替换当前的SynchronizationContext
(注意:永远不要在try{}
中使用await
——相反,如果需要,请将Task
存储在本地,并在恢复原始SynchronizationContext
后等待它(使用SynchronizationContextSwitcher.NoContext()
(源(这样的基元可能更安全,它可以做正确的事情(:
var originalSynchronizationContext = SynchronizationContext.Current;
try
{
SynchronizationContext.SetSynchronizationContext(null);
new MailController().CreateUser(user.userDetails);
}
finally
{
SynchronizationContext.SetSynchronizationContext(originalSynchronizationContext);
}
DeliverAsync是否返回Task?如果是,试试这个。
public class MailController : MailerBase
{
public async Task CreateUserAsync(ViewModel.VM_User user)
{
To.Add(user.EmailAddress);
From = System.Configuration.ConfigurationManager.AppSettings["emailSender"];
Subject = "Hi";
await (Email("CreateUser", user).DeliverAsync());
}
}
然后在控制器中,等待CreateUserAsync返回的任务。
public async Task<ActionResult> SaveUser(ViewModel.VM_CreateUser user)
{
var result = await _user.Save(userDetails);
await (new MailController().CreateUserAsync(user.userDetails));
return Json(new { Success = String.IsNullOrEmpty(result) });
}
注意:如果你的目标是让发送电子邮件成为后台火灾和遗忘操作,那就不是这样了。