在使用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秒内返回。

在使用WebForms Async/Await时访问响应对象

你的问题是,你试图访问请求上下文(即,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();
}