c# - 在 ASP.NET Web 窗体中管理长时间运行的任务的策略

标签 c# asp.net .net-4.5

我们有一些逻辑来为我们的 ASP.NET Web 窗体应用程序计算每个用户的昂贵值(value)。目前它位于每个页面上的 Page_Load header 用户控件中,如下所示:

//note that we are not awaiting this
Task.Factory.StartNew(() => CacheManager.GetExpensiveValue(UserId));

然后在静态 CacheManager.GetExpensiveValue(int userID) 中:

private static object locker = new object();

lock (locker)
{
    if (!AlreadyDone(userID))
    {
        var expensiveValue = ReallyExpensiveMethod(userID);
        //our static cache wrapper class that uses an ObjectCache object
        OurCache.Add(userID, expensiveValue);
    }
    else
    {
        return OurCache.Get(userID);
    }
}

这行得通,但是当 ReallyExpensiveMethod() 花费了很长时间(我也在努力提高其背后逻辑的性能)时,用户将阻塞那个 lock 在页面之间导航时。

我的问题是,我如何重组它才不会导致阻塞?我考虑过使用 ConcurrentDictionary,字典中的值是 ReallyExpensiveMethod() 的任务包装器,键是 UserID 以防止重复工作,但我不确定这是否真的能让我满意任何地方。

我们目前没有在这个应用程序中使用任何异步逻辑,我相信当权者宁愿不引入需要添加 Async="true" 到每个应用程序中的单个页面,因为此 header 逻辑在每个页面中。

最佳答案

My question is, how could I restructure this to not cause blocking? ... rather not introduce [asynchrony]

你进退两难。任何请求都必须阻塞或异步等待进程完成;没有其他选择,除非您可以使用 SignalR 之类的东西将过程结果发送到客户端(但这可能需要进行重大的架构更改)。

也就是说,您当然可以将 lock 的影响降到最低;如果一个用户正在执行此过程,它当前会阻止其他用户获取。

我假设这个计算是纯粹的(不会产生副作用),并且缓存是一个进程内的内存缓存。

在那种情况下,我会缓存任务而不是结果。虽然我不喜欢 ASP.NET 上的并行处理,但我想这没问题。

我建议您使用缓存。 ConcurrentDictionary具有类似的逻辑,但没有简单的方法来刷新旧条目。

所以,像这样:

// In Page_Load
CacheManager.GetOrAdd(UserID);

Task<Results> CacheManager.GetOrAdd(int userId)
{
  lock (locker)
  {
    if (!OurCache.Contains(userId))
    {
      var task = Task.Run(() => ReallyExpensiveMethod(userId));
      OurCache.Add(userId, task);
      return task;
    }
    else
      return OurCache.Get(userId);
  }
}

// Usage:
Results results = CacheManager.GetOrAdd(UserID).Result;

我对阻塞并不狂热(在最后一行调用 Task<T>.Result),但由于您不想执行异步请求,所以您遇到了这种 hack。

此代码最大限度地减少了持有锁的时间。它不是在处理期间锁定它,而是锁定足够长的时间以在另一个线程上开始处理并更新缓存。

关于c# - 在 ASP.NET Web 窗体中管理长时间运行的任务的策略,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23408710/

相关文章:

c# - 数据转换失败。 [ OLE DB 状态值(如果已知)= 2 ]

c# - 如何调试插件?

asp.net - Linq-to-SQL 数据上下文未生成对象

c# - .Net 4.5 wpf 打印问题,纸张大小始终为 NorthAmericaLetter

c# - 在 .NET 4.5 中创建配置节处理程序错误时发生错误

c# - 当 T 为类型时,使用 ServiceCollection 配置 IOptions<T>

c# - 在 C# 中将巨型 bool 数组保存/加载到磁盘?

c# - 从控件添加对 header 的引用

c# - ControlBuilder 实现的通用 DropDown 丢失了所有属性

performance - 如何在结果数有限的情况下对 IEnumerable 进行排序? (.OrderBy.Take 的另一个实现)