错误:异步模块或处理程序在异步操作仍处于挂起状态时完成 [英] Error: An asynchronous module or handler completed while an asynchronous operation was still pending

查看:484
本文介绍了错误:异步模块或处理程序在异步操作仍处于挂起状态时完成的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Controller Action方法可以保存用户详细信息,如下所示.

I have a Controller Action method to Save user Details like below.

public async Task<ActionResult> SaveUser(ViewModel.VM_CreateUser user)
{
    var result = await _user.Save(userDetails);
    return Json(new { Success = String.IsNullOrEmpty(result) });
}

到目前为止,以上4行功能都没有问题.

So far there is no issue in above 4 lines function.

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.
}

下面是我的邮件控制器.

and Below is my Mail Controller.

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();
    }
}

在上面的代码中,我在执行电子邮件发送代码时遇到问题.我收到以下错误消息.

in the above code, I am facing problem when I execute the Email Sending code. I get below error message.

异步完成的异步模块或处理程序 操作仍在等待中

An asynchronous module or handler completed while an asynchronous operation was still pending

请提出纠正措施!

推荐答案

这是因为async方法跟踪其完成,甚至是async void.它们通过在启动时向SynchronizationContext注册并在返回时将操作标记为完成来完成此操作. ASP.NET会跟踪所有已创建的操作,并要求它们在操作返回之前完成所有操作,否则它将向HTTP客户端返回错误.如果需要运行即发即弃"方法,则必须手动避免在ASP.NET SynchronizationContext上启动它.例如,await Task.Run(() => CallFireAndForget())(await,以便您等待同步部分在线程池上运行.这行得通,因为CallFireAndForget()async void.触发一个将Task作为fire-and返回的方法-忘记,您必须明确避免将Task返回到Task.Run(),如下所示:await Task.Run(() => { CallFireAndForgetAsync(); })).

This is because async methods track their completion, even async void. They do this by registering with the SynchronizationContext when starting and marking the operation complete when returning. ASP.NET tracks all created operations and requires them to all be completed before your action returns, otherwise it returns an error to the HTTP client. If you need to run a "fire and forget" method, you must manually avoid launching it on the ASP.NET SynchronizationContext. For example, await Task.Run(() => CallFireAndForget()) (await so that you wait for the synchronous portion to run on the thread pool. This works because CallFireAndForget() is async void. To fire a method which returns a Task as fire-and-forget, you have to explicitly avoid returning the Task to Task.Run() like this: await Task.Run(() => { CallFireAndForgetAsync(); })).

获取相同错误消息以显示的另一种方法应该是在异步操作中编写此消息:

Another way to get the same error message to show up should be to write this in an asynchronous action:

// BAD CODE, DEMONSTRATION ONLY!
AsyncOperationManager.CreateOperation();

如果使用AsyncOperation API,则必须确保在允许操作方法返回之前先调用AsyncOperation.OperationCompleted().

If you use the AsyncOperation API, you have to ensure you call AsyncOperation.OperationCompleted() prior to allowing the action method to return.

在您的情况下,如果邮件发送确实是一劳永逸的任务,则在将其签名更改为async Task CreateUserAsync(ViewModel.VM_User user)后,可以在CreateUser内部执行以下操作:

In your case, if the mail sending is really intended to be a fire-and-forget task, you can do the following inside of CreateUser after changing its signature to async Task CreateUserAsync(ViewModel.VM_User user):

await Task.Run(() => Email("CreateUser", user).DeliverAsync());

以及您的操作:

await new MailController().CreateUserAsync(user.userDetails);

理想情况下,您不会将Task.Run()用于此类操作.取而代之的是,您可以临时替换当前的SynchronizationContext((注意:从不await放在try{}内-相反,如果需要,请将Task存放在本地,然后在恢复原始SynchronizationContext (使用诸如

Ideally, you wouldn’t use Task.Run() for something like this. Instead, you can temporarily replace the current SynchronizationContext instead (NOTE WELL: Never await inside the try{}—instead, if you need to, store the Task in a local and await it after restoring the original SynchronizationContext (it may be safer to use a primitive such as SynchronizationContextSwitcher.NoContext() (source) which does the right thing)):

var originalSynchronizationContext = SynchronizationContext.Current;
try
{
    SynchronizationContext.SetSynchronizationContext(null);
    new MailController().CreateUser(user.userDetails);
}
finally
{
    SynchronizationContext.SetSynchronizationContext(originalSynchronizationContext);
}

这篇关于错误:异步模块或处理程序在异步操作仍处于挂起状态时完成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆