【发布时间】:2017-12-12 22:56:57
【问题描述】:
所以我在 ASP.NET Web API 应用程序中有以下异步方法的程序流程:API 控制器调用异步方法(我们称之为 asyncMethod1),它需要第二个异步的东西方法(我们称之为 asyncMethod2)。 API 控制器实际上并不需要知道 asyncMethod1 何时完成,尽管它会很好地为用户提供反馈。另一方面,asyncMethod1 确实需要知道 asyncMethod2 何时完成。
以下是相关代码:
public class MyController : ApiController
{
private CustomClass customClass;
public IHttpActionResult WorkMethod(TestObject testObject)
{
Debug.WriteLine("WorkMethod - before calling asyncMethod1");
Task.Run(async () => await this.asyncMethod1(testObject)).Wait(); // ERROR HERE
Debug.WriteLine("WorkMethod - after calling asyncMethod1");
return Ok();
}
private async Task asyncMethod1(TestObject testObject)
{
Debug.WriteLine("asyncMethod1 - before calling asyncMethod2");
Dictionary<string, string> x = await this.asyncMethod2(testObject);
Debug.WriteLine("asyncMethod1 - after calling asyncMethod2");
try
{
using (Task<IList<TestResponse>> testTask = customClass.DoAsyncWork1(x))
{
IList<TestResponse> response = await testTask;
// Do something with response
Debug.WriteLine("Transform 'response'");
}
}
catch (Exception ex)
{
throw new Exception(" Exception ", ex);
}
}
private async Task<Dictionary<string, string>> asyncMethod2(TestObject testObject)
{
try
{
Debug.WriteLine("asyncMethod2 - before calling DoAsyncWork2");
using (Task<IList<SomeData>> workTask = customClass.DoAsyncWork2(testObject.x))
{
IList<SomeData> data = await workTask ;
var output = new Dictionary<string, string>();
output = data.values;
Debug.WriteLine("asyncMethod2 - returning data to caller");
return output;
}
}
catch (Exception ex)
{
throw new Exception(" Exception ", ex);
}
}
}
CustomClass 是我正在使用的自定义 API 中的一个类,其中方法 DoAsyncWork1 和 DoAsyncWork2 都是异步的。
当上面的代码运行时,我在方法WorkMethod中得到以下异常,行Task.Run(async () => await this.asyncMethod1(testObject)).Wait();:
"Message":"发生错误。","ExceptionMessage":"发生一个或多个错误。","ExceptionType":"System.AggregateException","StackTrace":" at
System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
在 System.Threading.Tasks.Task.Wait(Int32 毫秒超时,CancellationToken cancelToken)
在 System.Threading.Tasks.Task.Wait()
在 MyController.cs 中的 MyController.WorkMethod(Test testObject):第 9 行
在 lambda_method(Closure , Object , Object[] )
在 System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.c__DisplayClass10。 b__9(Object instance, Object[] methodParameters)
在 System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
在 System.Web.Http.Controllers.ReflectedHttpActionDescriptor。 ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancelToken)
输出如下:
WorkMethod - 调用 asyncMethod1 之前
asyncMethod1 - 调用 asyncMethod2 之前
asyncMethod2 - 调用 DoAsyncWork2 之前
asyncMethod2 - 向调用者返回数据
asyncMethod1 - 调用 asyncMethod2 之后
如果,在 WorkMethod 方法中我改变了这个:
Task.Run(async () => await this.asyncMethod1(asyncMethod1)).Wait();
到这里:
var task = this.asyncMethod1(asyncMethod1);
Debug.WriteLine("WorkMethod - after calling asyncMethod1");
task.wait();
我没有遇到异常,但程序流永远不会到达 asyncMethod1 中的点,它对从 asyncMethod2 返回的响应执行一些工作。另外,输出如下:
WorkMethod - 调用 asyncMethod1 之前
asyncMethod1 - 调用 asyncMethod2 之前
asyncMethod2 - 调用 DoAsyncWork2 之前
WorkMethod - 调用 asyncMethod1 之后
这里有什么问题?什么是我想要实现的正确方法,即从 API 控制器调用异步方法,然后从第一个异步方法调用第二个异步方法?
【问题讨论】:
-
您永远不应该阻止使用
Wait()或.Result的异步调用。为什么WorkMethod不是异步的?Task.Run()会做哪些工作?如果工作特别受 IO 限制,那你就是在浪费线程。 -
@Crowcoder 我对此没有特别好的答案,除了以下我发现非异步方法调用异步方法的示例。调用异步方法的方法是否也必须是异步的?在链上的某个时刻,你必须有一个非异步方法,对吧?!它们不可能都是异步的。至于你的第二个问题,正在做的工作是 Task.Run() 是调用 asyncMethod1。
-
在 web api 中,保持一切异步应该很容易,因为它不会影响调用它的客户端。如果你不能让它全部异步,那么最好让它全部同步,因为将两者混合会导致资源使用效率低下和死锁风险。我看到它调用
asyncMethod1,但显然它没有充实,所以我不知道你将在该方法中实际做什么。 -
@Crowcoder 它不可能全部同步,因为我依赖于另一个 API,其方法是异步的。因此,根据您所说的,它必须全部是异步的。你是对的,WorkMethod 还没有完成,但我唯一需要做的就是让用户知道操作是否成功。
-
请记住,您必须等待任何呼叫,您不能执行“一劳永逸”并仍将任何结果报告给用户。
标签: c# asp.net asp.net-web-api async-await