错误:异步模块或处理程序已完成,而异步操作仍处于挂起状态

本文关键字:异步操作 挂起状态 已完成 异步 模块 程序 处理 错误 | 更新日期: 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) });
    }

注意:如果你的目标是让发送电子邮件成为后台火灾和遗忘操作,那就不是这样了。