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

big*_*mac 0 c# asp.net webforms async-await

我有一个简单的.net 4.5 webforms项目,我用它来输出一些服务检查的结果.我已经创建了一个多线程和等待的服务检查类(每次检查可能需要1-3秒,我希望它们并行检查).我希望每个服务检查的结果都写入网页并在确定后立即刷新(我不关心结果是否有序).服务检查程序方法已经过测试,在控制台应用程序中运行良好,但我无法将其移植到webforms应用程序.

下面的代码有点工作(非常随机).有时,它是完美的.其他时候,它会"跳过"显示其中一个测试的结果.其他时候,它将CSS样式的显示与结果混合在一起!但是,大多数情况下,它在CheckService中的Response.Flush行崩溃,说明"重叠的I/O操作正在进行中",然后崩溃.

如何在每次检查完成时编写网页响应缓冲区并以稳定的方式显示其结果?

这是aspx页面的代码......

<%@ Page Language="C#" Async="true" AutoEventWireup="true" Inherits="ServiceMonitor._Default" Codebehind="Default.aspx.cs" %>
Run Code Online (Sandbox Code Playgroud)

这是背后的代码......

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; }
}
Run Code Online (Sandbox Code Playgroud)

仅供参考 - 应用程序不应该很漂亮,因此删除了标准的HTML标记.它也不会被许多用户访问(实际上只是我),因此IIS阻塞并不是真正的问题,页面应该在20-30秒内返回.

Ste*_*ary 5

您的问题是您正在尝试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;
}
Run Code Online (Sandbox Code Playgroud)

它可以被称为同时,没有多线程:

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();
}
Run Code Online (Sandbox Code Playgroud)

  • @bigmac:这是因为真正的异步操作不需要线程运行.我在[我的博客](http://blog.stephencleary.com/2013/11/there-is-no-thread.html)上对此进行了描述,但以HTTP ping为例:一旦发送了HTTP请求,只需要等待响应就不需要线程就可以了.所以你可以使用`async` /`await`来启动三个请求,然后使用`await Task.WhenAll`来(异步)等待它们全部完成.没有线程被阻止,但代码同时执行多个操作. (3认同)
  • 只是为了让您知道我完成了重构并且您的代码运行得很好!谢谢您的帮助! (2认同)