我们有一些逻辑来为我们的ASP.NET Web窗体应用程序计算每个用户的昂贵价值.目前它位于每个页面上的Page_Load标头用户控件中,如下所示:
//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()花了很长时间(我也在努力提高其背后的逻辑性能)时,用户将在页面之间导航时阻止该锁定.
我的问题是,我怎么能重组这个不导致阻塞?我已经考虑过使用ConcurrentDictionary,字典中的值是ReallyExpensiveMethod()的Task包装器,而键是UserID来防止重复工作,但我不确定这是否真的让我到处都是.
我们目前在这个应用程序中没有使用任何异步逻辑,而且我确信这些功能不会引入需要向应用程序中的每个页面添加Async =“true”的更改,因为此标头逻辑是在每一页.
解决方法:
My question is, how could I restructure this to not cause blocking? … rather not introduce [asynchrony]
你在那里的岩石和坚硬的地方之间.任何请求都必须阻止或异步等待进程完成;没有其他选择,除非您可以使用SignalR之类的东西将流程结果发送到客户端(但这可能需要进行重大的架构更改).
也就是说,你当然可以最大限度地减少锁的影响;如果一个用户正在执行该过程,它目前阻止其他用户获取.
我假设这个计算是纯粹的(没有副作用),并且缓存是一个进程内的内存缓存.
在这种情况下,我会缓存任务而不是结果.虽然我对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),但由于你不想做异步请求,你会遇到这种黑客攻击.
此代码最大限度地减少了锁定的时间.它不是在处理期间锁定它,而是仅锁定足够长的时间以在另一个线程上启动处理并更新缓存.
标签:c,asp-net,net-4-5 来源: https://codeday.me/bug/20190528/1174059.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。