在使用WebForms Async/Await时访问响应对象
本文关键字:访问 响应 对象 Await WebForms Async | 更新日期: 2023-09-27 18:18:46
我有一个简单的。net 4.5 webforms项目,我用它来输出一些服务检查的结果。我已经创建了一个多线程和可等待的服务检查类(每次检查可能需要1-3秒,我希望它们并行检查)。我希望每个服务检查的结果被写入网页,并在确定后立即刷新(我不在乎结果是否有序)。服务检查器方法已经经过测试,在控制台应用程序中运行良好,但我在将其移植到webforms应用程序时遇到了麻烦。
下面的代码可以正常工作(非常随机)。有时候,它是完美的。其他时候,它会"跳过"显示其中一个测试的结果。其他时候,它将CSS样式的显示与结果混合在一起!不过,大多数情况下,它会在响应时崩溃。CheckService中的刷新行显示"重叠I/O操作正在进行中",然后它崩溃了。
我如何写网页响应缓冲区时,每次检查完成,并以稳定的方式显示他们的结果?
这是aspx页面的代码…
<%@ Page Language="C#" Async="true" AutoEventWireup="true" Inherits="ServiceMonitor._Default" Codebehind="Default.aspx.cs" %>
下面是代码…
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Response.Write("<style type='"text/css'">body {font-family: Arial;font-size: 10pt;}</style>");
Response.Write("Beginning processing...<br/>");
Response.Flush();
RegisterAsyncTask(new PageAsyncTask(CheckServices));
}
protected async Task CheckServices()
{
Response.Write(string.Format("Starting checks at {0}<br/>", DateTime.Now.ToLongTimeString()));
var tasks = new List<Task<ServiceStatus>>
{
Task.Run(() => CheckService("ServiceOne")),
Task.Run(() => CheckService("ServiceTwo")),
Task.Run(() => CheckService("ServiceThree"))
};
var checkResults = (await Task.WhenAll(tasks)).ToList();
Response.Write(string.Format("Checking complete at {0}<br/>", DateTime.Now.ToLongTimeString()));
Response.Flush();
}
public ServiceStatus CheckService(string serviceName)
{
var startTime = DateTime.Now;
// Simulate a longer running process, by pausing for 1-3 seconds
var random = new Random();
var end = DateTime.Now + TimeSpan.FromMilliseconds(random.Next(1000, 3000));
while (DateTime.Now < end) { }
var elapsedTime = (DateTime.Now - startTime).TotalSeconds;
var service = new ServiceStatus {Name = serviceName, IsRunning = true};
Response.Write(string.Format("Done with {0} in {1} seconds<br/>", service.Name, elapsedTime.ToString("N2")));
Response.Flush();
return service;
}
}
public class ServiceStatus
{
public string Name { get; set; }
public bool IsRunning { get; set; }
}
供参考-应用程序不应该是漂亮的,因此删除了标准的HTML标记。它也不会被很多用户访问(实际上只有我),所以IIS阻塞不是一个真正的问题,页面应该在20-30秒内返回。
你的问题是,你试图访问请求上下文(即,Reponse.Write
)从一个随机的后台线程(即,Task.Run
)。
理想的解决方案是始终采用异步。这样,我的意思是使您的服务检查器异步,但不是多线程。如果它们是使用API ping实现的,那么你可以检查HttpClient
的实现。
一旦它们是异步的,CheckService
方法也将是异步的:
public async Task<ServiceStatus> CheckServiceAsync(string serviceName)
{
var startTime = DateTime.Now;
// Simulate a longer running process, by pausing for 1-3 seconds
var random = new Random();
var waitTime = TimeSpan.FromMilliseconds(random.Next(1000, 3000));
await Task.Delay(waitTime);
var elapsedTime = (DateTime.Now - startTime).TotalSeconds;
var service = new ServiceStatus {Name = serviceName, IsRunning = true};
Response.Write(string.Format("Done with {0} in {1} seconds<br/>", service.Name, elapsedTime.ToString("N2")));
Response.Flush();
return service;
}
可以并发调用,不需要多线程:
protected async Task CheckServicesAsync()
{
Response.Write(string.Format("Starting checks at {0}<br/>", DateTime.Now.ToLongTimeString()));
var tasks = new List<Task<ServiceStatus>>
{
CheckService("ServiceOne"),
CheckService("ServiceTwo"),
CheckService("ServiceThree")
};
var checkResults = (await Task.WhenAll(tasks)).ToList();
Response.Write(string.Format("Checking complete at {0}<br/>", DateTime.Now.ToLongTimeString()));
Response.Flush();
}