【问题标题】:How to wait for a Task<T> to complete in Web API controller如何在 Web API 控制器中等待 Task<T> 完成
【发布时间】:2014-08-05 16:11:18
【问题描述】:

我正在尝试使用返回异步任务的方法。方法是:

    public async Task<DataResponse[]> GetGridDataAsync(DataRequest[] requestCollection, object state)
    {
        return await Task.Factory.FromAsync<DataResponse[]>(BeginGetDataAsync(requestCollection, ar => EndGetDataAsync(ar), state), 
            EndGetDataAsync);
    }

在 Web API 控制器中使用它时,90% 的时间都会出现以下错误:

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

我的尝试是:

    [HttpGet]
    [Route("GetGridDataAsync")]
    public IEnumerable<DataResponse> GetGridDataAsync()
    {
        var proxy = new Proxy();
        var task = proxy.GetGridDataAsync(dataRequest, new object());

        if (!task.IsCompleted)
        {
            task.Start();
            task.Wait();                
        }                        

        //var x = (IAsyncResult)task; //i also tried this
        //task.Start();
        //x.AsyncWaitHandle.WaitOne();

        return task.Result;
    }

如何在请求完成(或至少指定超时)之前阻塞线程?我将从 Angular 使用这个端点。

这是我上一个问题的后续:Have Web API controller wait for IAsyncResult before completing?

更新 1: 返回 proxy.GetGridDataAsync(dataRequest, new object()) 引发了异常。我可以单步执行 BeginXXX 函数,但 EndXXX 中的断点永远不会被击中。不知道为什么。

有时我没有收到上面的“异步模块或处理程序...”错误,并在堆栈跟踪中收到不同的错误。但我不确定这意味着什么:

{  
"Message":"An error has occurred.",
"ExceptionMessage":"Object cannot be null.",
"ExceptionType":"System.ArgumentNullException",
"StackTrace":" at 
System.Threading.ThreadPool.RegisterWaitForSingleObjectNative(WaitHandle waitHandle, Object state, UInt32 timeOutInterval, Boolean executeOnlyOnce, RegisteredWaitHandle registeredWaitHandle, StackCrawlMark& stackMark, Boolean compressStack)
at System.Threading.ThreadPool.RegisterWaitForSingleObject(WaitHandle waitObject, WaitOrTimerCallback callBack, Object state, UInt32 millisecondsTimeOutInterval, Boolean executeOnlyOnce, StackCrawlMark& stackMark, Boolean compressStack)
at System.Threading.ThreadPool.RegisterWaitForSingleObject(WaitHandle waitObject, WaitOrTimerCallback callBack, Object state, Int32 millisecondsTimeOutInterval, Boolean executeOnlyOnce)
at System.Threading.Tasks.TaskFactory`1.FromAsyncImpl(IAsyncResult asyncResult, Func`2 endFunction, Action`1 endAction, TaskCreationOptions creationOptions, TaskScheduler scheduler, StackCrawlMark& stackMark)
at System.Threading.Tasks.TaskFactory.FromAsync[TResult](IAsyncResult asyncResult, Func`2 endMethod)
at proxy.d__1.MoveNext() in proxy.cs:line 30
--- End of stack trace from previous location where exception was thrown ---
}

【问题讨论】:

标签: c# .net asp.net-web-api task-parallel-library async-await


【解决方案1】:

更改您的控制器以返回像这样的Task&lt;IEnumerable&lt;DataResponse&gt;&gt;...

[HttpGet]
[Route("GetGridDataAsync")]
public async Task<IEnumerable<DataResponse>> GetGridDataAsync()
{
    var proxy = new Proxy();
    return await proxy.GetGridDataAsync(dataRequest, new object());
}

【讨论】:

  • 为什么要等待,为什么不直接标记函数async,而是保留返回类型Task&lt;IEnumerable&lt;DataResponse&gt;&gt;,直接返回任务?
  • 我同意您的更改,但我仍然收到错误:异步模块或处理程序已完成,而异步操作仍处于挂起状态
【解决方案2】:

当你调用一个async方法时,推荐await它。在您的方法中,您实际上是阻塞直到任务完成,这与同步运行它完全一样。

如果你只想在GetGridDataAsync完成时返回,你需要标记你的方法async,让它返回一个Task&lt;IEnumerable&lt;DataResponse&gt;&gt;await它:

[HttpGet]
[Route("GetGridDataAsync")]
public async Task<IEnumerable<DataResponse>> GetGridDataAsync()
{
    var proxy = new Proxy();
    return await proxy.GetGridDataAsync(dataRequest, new object());
}

如果您不需要等到GetGridDataAsync 完成,您可以自己保存状态机生成并简单地返回Task

[HttpGet]
[Route("GetGridDataAsync")]
public Task<IEnumerable<DataResponse>> GetGridDataAsync()
{
    return proxy.GetGridDataAsync(dataRequest, new object());
}

另外,请注意您的 GetGridDataAsync 还会创建一个冗余状态机,因为它对等待的结果没有任何作用。你可以简单地返回没有await的结果,让调用者await就可以了:

public async Task<DataResponse[]> GetGridDataAsync(DataRequest[] requestCollection, object state)
{
    return Task.Factory.FromAsync<DataResponse[]>(BeginGetDataAsync(requestCollection, ar => EndGetDataAsync(ar), state), 
        EndGetDataAsync);
}

【讨论】:

  • 我将从 Angular JS 中使用它。现在我只是通过从浏览器直接点击控制器的 hte URL 进行测试。所以我可以简单地等待结果。但是,在实现这些更改时,我仍然收到错误“异步模块或处理程序已完成,而异步操作仍处于挂起状态”。 :(
  • 首先,如果你从angular使用它,请确保使用GetGridDataAsyncreturn await版本。二、tge异常在哪一行被抛出?
  • @Hoppe 在没有FromAsync 的情况下,您的BeginXXXEndXXX 是否正常工作?我认为那个电话可能有问题
猜你喜欢
  • 2020-01-07
  • 1970-01-01
  • 2014-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多