某些类只有在HTTP请求中调用类中的某些方法时才需要初始化某些应用程序范围的资源。
此外,初始化会从某些HTTP / REST服务请求某些数据。此外,服务资源是与异步模式(async
/ await
)异步请求的。
目前,我一直阻止所有请求,直到初始化结束时使用ReaderWriterLockSlim
和TryEnterWriteLock
预定义超时。
我的问题是,有时TryEnterWriteLock
以死锁结束,即使没有写锁定。
我的假设是,当一些等待结束其异步操作时,无法保证,由于线程和SynhcronizationContext
在ASP.NET上的工作方式,阻塞的代码会在同一个线程中继续存在,意味着永远不会退出写锁定。
也许我可以将整个代码移动到Global.asax Application_Start
事件处理程序,但重点是我不希望每个应用程序生命周期初始化整个资源一次,因为请求的资源已到期并且可能会再次要求加班。
这种情况的正确方法是什么,其中异步代码和其他传入HTTP请求应该等到拥有异步等待初始化的请求,而不是在所谓的死锁中结束吗
答案 0 :(得分:1)
两种可能的解决方案:
利用Lazy<>
对象(您没有告诉我们资源是否是“单一”(例如文件),是否是每个用户,或者它是否类似于每个可能的字典键/值必须懒惰加载。
有ConfigureAwait方法......
MyObject result = await task.ConfigureAwait(true)
现在结果将在原始线程上返回。如果使用相同的方法进行锁定,则锁定将正确释放
答案 1 :(得分:1)
我有entry on my blog that deals with "asynchronous construction"。我更喜欢“异步工厂”方法,但听起来它不适合您的情况,因为您的资源是应用程序范围内的共享。
在这种情况下,我建议启用AsyncLazy<T>
,Lazy<T>
只启用async
(即工厂方法可以是async
,消费者可以await
初始化完成)。如果您的资源作为静态属性公开,AsyncLazy<T>
是一个很好的匹配。
但是,如果您正在使用DI / IoC,那么您确实需要(同步)构造(至少在DI / IoC库支持某种形式的异步构造之前)。在这种情况下,您可以使用“异步初始化”代码模式。
或者,您可以使用我在AsyncEx
library中开发的AsyncReaderWriterLock
。但我认为其他方法的意图更为明确。
答案 2 :(得分:0)
有时解决方案就在你面前,但你只是蒙蔽了眼睛!
阻止所有传入请求,直到获取资源为止?为什么?错了,错了,错了!
有一种称为缓存的方法和一种称为ASP.NET缓存的已知API,并且有一种方法Cache.Insert
接受缓存更新回调被超时调用给定的时间间隔...
所谓的缓存更新回调在一个单独的线程中运行。它只是在缓存更新到期之前的一段时间配置缓存更新,并且不会阻止任何请求:用户和机器将继续工作而不会中断!
哦,应该在Cache.Insert(...)
类的Application_Start
事件处理程序中调用Global.asax
。
我希望我的问题和答案都可以作为对他人的建议,以避免错误的线程/阻止做法......