我已经在ApiController中表达了一种简单的async方法(我认为):
public class CommentsController : ApiController
{
private static readonly IList<CommentModel> Comments;
static CommentsController()
{
// Note: Removed to abbreviate - populate comments manually here.
}
[ResponseType(typeof (IList<CommentModel>))]
[HttpGet]
public async Task<IHttpActionResult> GetAllComments()
{
return await Task.Factory.StartNew(() =>
{
Thread.Sleep(10000); // Breakpoint #1
return Ok(Comments); // Breakpoint #2
});
// Breakpoint #3
}
}
请注意我上面提到的断点。
我期望发生的事情是在我点击继续时排在第一位,让线程等待但是流程在此阶段继续通过#3。
然后当睡眠结束时,再次继续并在#2处休息。
然而,在调试过程中它似乎是同步的。
我的问题首先是这是真正的异步,如何调试它来验证,或者使用单元测试?
答案 0 :(得分:3)
然而,在调试过程中它似乎是同步的。
它是串行(非同步),因为await
关键字告诉方法在该操作完成之前不会继续。调试器将以串行方式逐步执行异步方法,这可以使它们看起来是同步的;这比使调试器跳转到不相关的代码然后稍后再跳回来更自然。
我的问题首先是这是真正的异步
这是假的异步。在现实世界的代码中,您永远不会使用Task.Factory.StarNew
。
此外,对于ASP.NET应用程序,您应该避免将工作发送到线程池(StartNew
,Task.Run
等)。相反,您应该调用自然异步API。
如何调试它以验证或以其他方式进行单元测试?
您可以调用该方法,验证任务是否尚未完成,然后等待它。请注意,为避免竞争条件,您应该删除控制器正在使用的任何异步服务。
答案 1 :(得分:2)
断点3 永远不会被击中,也不应该被击中。
以Task
开头的Task.Factory.StartNew
将以异步方式运行
但是await
基本上等待Task
在继续当前方法之前完成执行,并将执行返回给调用者。因此,它会为给定的Task
添加延续
由于您返回Task
的结果,断点3 将永远不会执行。
如果你想在Task
之后执行 Breakpoint 3 ,你基本上有两个选择:
您可以添加续集:
return await Task.Factory.StartNew(() =>
{
Thread.Sleep(10000); // Breakpoint #1
return Ok(Comments); // Breakpoint #2
}).ContinueWith((Task<IHttpActionResult> t) =>
{
//t.Result contains the result of the previous Task
return t.Result; // Breakpoint #3
});
或(更简单),使用临时值和await
:
var temp = await Task.Factory.StartNew(() =>
{
Thread.Sleep(10000); // Breakpoint #1
return Ok(Comments); // Breakpoint #2
});
// Breakpoint #3
return temp;
要在不等待执行完成的情况下调用async
方法,请忽略await
。所以,如果你想并行执行GetAllComments
5次,你可以这样做:
for(int i = 0; i < 5; i++)
GetAllComments();
答案 2 :(得分:1)
Breakpoint#3将如何被击中?你在等待之前得到了回报。
所以,会发生的事情是await会产生一个状态机,并且在完成你的线程后只会返回该值。