我有一个循环,实际上等待一些完成Job的过程并返回结果。
我可以使用MyRestClient.FetchResult(id)
和MyRestClient.FetchResultAsync(id)
,它从某些远程服务获取结果,如果完成则返回布尔值。
public class StatusController: ActionController {
public ActionResult Poll(long id){
return new PollingResult(()=>{
return MyRestClient.FetchResult(id) == SomethingSuccessful;
});
}
}
public class PollingResult : ActionResult{
private Func<bool> PollResult;
public PollingResult(Func<bool> pollResult){
this.PollResult = pollResult;
}
public override void ExecuteResult(ControllerContext context)
{
Response = context.HttpContext.Response;
Request = context.HttpContext.Request;
// poll every 5 Seconds, for 5 minutes
for(int i=0;i<60;i++){
if(!Request.IsClientConnected){
return;
}
Thread.Sleep(5000);
if(PollResult()){
Response.WriteLine("Success");
return;
}
// This is a comet, so we need to
// send a response, so that browser does not disconnect
Response.WriteLine("Waiting");
Response.Flush();
}
Response.WriteLine("Timeout");
}
}
现在我只是想知道是否还有使用Async Await来改进这个逻辑,因为这个线程只是每5秒等待5分钟。
更新
异步任务模式通常在将结果发送回客户端之前完成所有工作,请注意,如果我在5秒内没有将中间响应发送回客户端,则客户端将断开连接。
客户端长轮询的原因
我们的网络服务器位于高速互联网上,其他客户端处于低端连接,从客户端到我们的服务器进行多次连接,然后进一步转发到第三方api,这对客户端来说几乎没有额外开销。
这称为Comet技术,而不是在5秒的持续时间内进行多次调用,保持连接打开的时间更长,资源消耗更少。
当然,如果客户端断开连接,客户端将重新连接并再次等待。与单个轮询请求相比,每5秒多个HTTP连接可以更快地耗尽电池寿命
答案 0 :(得分:6)
首先,我应该指出SignalR旨在取代手动长轮询。如果可能,我建议您先使用它。如果双方都支持它,它将升级到WebSockets,这比长轮询更有效。
没有&#34; async ActionResult&#34;在MVC中受支持,但您可以执行类似via a trick:
的操作public async Task<ActionResult> Poll()
{
while (!IsCompleted)
{
await Task.Delay(TimeSpan.FromSeconds(5));
PartialView("PleaseWait").ExecuteResult(ControllerContext);
Response.Flush();
}
return PartialView("Done");
}
然而,冲洗部分结果完全违背了MVC的精神和设计。 MVC =模型,视图,控制器,你知道。 Controller构造Model并将其传递给View。在这种情况下,您可以让Controller直接刷新View的部分内容。
WebAPI有一个更自然,更少hackish的解决方案:PushStreamContent
类型,with an example。
MVC绝对不是为此而设计的。 WebAPI支持它,但不是主流选项。如果您的客户可以使用SignalR,SignalR是适当的技术。
答案 1 :(得分:4)
使用Task.Delay
代替Thread.Sleep
await Task.Delay(5000);
Sleep
告诉操作系统让你的线程进入休眠状态,并将其从调度中移除至少5秒钟。如下所示,该线程将在5秒内不执行任何操作 - 这可以用于处理传入请求的线程少一个。
await Task.Delay
创建一个计时器,该计时器将在5秒后打勾。问题是,这个计时器doesn't use a thread itself - 它只是告诉操作系统在5秒钟后发出一个ThreadPool线程的信号。
同时,您的主题可以自由回答其他请求。
<强>更新强>
对于您的特定情况,似乎有一个问题。
通常,您更改周围方法的签名以返回Task
/ Task<T>
而不是void。但ASP.NET MVC不支持异步ActionResult
(请参阅here)。
您似乎可以选择:
答案 2 :(得分:1)
然而,我正在使用第三方云API进行视频编码 我的网络客户端(chrome / ie / ff)需要轮询编码结果。如果我 只需每隔5秒传递一次结果,Web客户端就需要 一个接一个地进行多个HTTP调用
我认为当您尝试在单个HTTP请求的边界内(即在ASP.NET MVC控制器方法中)轮询视频编码操作的结果时,这种方法是错误的。
在进行轮询时,客户端浏览器仍在等待您的HTTP响应。这样,客户端HTTP请求可能很容易超时。它也是一种不那么用户友好的行为,用户没有收到任何进度通知,也无法请求取消。
我最近回答了一个关于long-running server side operation的相关问题。 IMO,处理它的最佳方法是将其外包给WCF服务并使用AJAX轮询。我还在how to do the asynchronous long-polling in a WCF service上回答了另一个相关问题。