1

某些类仅当在 HTTP 请求中调用该类中的某些方法时才需要初始化某些应用程序范围的资源。

此外,初始化会从一些 HTTP/REST 服务请求一些数据。此外,使用异步模式 ( async/ await) 异步请求服务资源。

目前,我一直在阻止所有请求,直到使用 aReaderWriterLockSlimTryEnterWriteLock预定义的超时结束初始化。

我的问题是有时会TryEnterWriteLock以死锁结束,即使没有持有写锁。

我的假设是,不能保证一旦某个awaitable结束其异步操作,由于线程和SynhcronizationContext在 ASP.NET 上的工作方式,被阻塞的代码会在同一个线程中继续,这意味着写锁永远不会退出。

也许我可以将整个代码移动到 Global.asaxApplication_Start事件处理程序,但关键是我不想在每个应用程序生命周期中初始化整个资源一次,因为请求的资源已过期并且可能会再次超时请求。

整个问题...

在这种情况下,有异步代码和其他传入的 HTTP 请求应该等到拥有异步等待初始化的请求而不以所谓的死锁结束的情况下,正确的方法是什么?

4

3 回答 3

1

两种可能的解决方案:

  • 利用Lazy<>对象(您还没有告诉我们资源是否是“单一的”(例如文件),是否是每个用户的,或者它是否类似于字典,其中每个可能的键/值都必须延迟加载。

  • ConfigureAwait方法...

MyObject 结果 = 等待 task.ConfigureAwait(true)

现在结果将在原始线程上返回。如果你用同样的方法做锁,锁将被正确释放

于 2013-08-14T06:13:02.540 回答
1

我的博客上有一篇关于“异步构造”的文章。我更喜欢“异步工厂”方法,但这听起来不适合您的情况,因为您的资源是在应用程序范围内共享的。

在这种情况下,我推荐AsyncLazy<T>,它只是一个启用的(Lazy<T>async工厂方法可以是async,而消费者可以await为初始化完成)。AsyncLazy<T>如果您的资源作为静态属性公开,则非常适合。

但是,如果您使用 DI/IoC,则确实需要(同步)构造(至少在 DI/IoC 库支持某种形式的异步构造之前)。在这种情况下,您可以使用“异步初始化”代码模式。

或者,您可以使用AsyncReaderWriterLock我作为AsyncEx的一部分开发的。但我认为其他方法的意图更明确。

于 2013-08-14T12:40:01.680 回答
0

有时解决方案就在您面前,但您只是盲目!

在获取资源之前阻止所有传入请求?为什么?错,错,错!

有一种称为缓存的方法和一种称为 ASP.NET 缓存的已知 API,还有一种方法Cache.Insert接受在给定时间间隔内超时调用的缓存更新回调...

再次:不要阻止,不要阻止!

所谓的缓存更新回调在单独的线程中运行。它只是在缓存更新到期前的某个时间配置缓存更新,不会阻止任何请求:用户和机器将继续工作而不会中断!

哦,Cache.Insert(...)应该在类的Application_Start事件处理程序中调用Global.asax

我希望我的问题和答案都可以作为对其他人的建议,以避免不良的线程/阻塞做法......

于 2013-08-14T11:03:01.933 回答