lock(syncObject) 什么时候可以抛出异常?
-
06-07-2019 - |
题
我在 .NET 中编写了一个 com 组件,如果我尝试锁定任何方法中的任何对象(由与我的 com 组件通信的非托管代码调用),我会收到异常。
我目前没有异常的确切文本,但它也没有多大帮助。
所以我的问题是在什么情况下 lock(syncObject) 会抛出异常?以下是一些事实:
- 同步对象不为空
- syncObject 尚未锁定
它与在 STA(单线程单元)或 MTA(多线程单元)中运行的被调用者有什么关系吗?
解决方案
从 这一页:
每次锁获取都可能引发异常。做好准备。
如果锁获取遇到争用,大多数锁都会延迟分配事件,包括 CLR 监视器。在资源不足的情况下,此分配可能会失败,从而导致源自锁入口的 OOM。(请注意,典型的非阻塞自旋锁不会因 OOM 而失败,这使得它可以在某些资源受限的场景中使用,例如在 CER 内。)类似地,像 SQL Server 这样的主机可以执行死锁检测,甚至可以通过以下方式打破这些死锁:生成源自 Enter 语句的异常,表现为 System.Runtime.InteropServices.COMException。
通常,对于此类异常,我们无能为力。但是,必须稳健地处理故障的可靠性和安全性敏感的代码应该考虑这种情况。我们希望能够智能地响应主机死锁,但大多数库代码无法智能地展开到堆栈上的安全点,以便它可以后退并重试操作。在典型的堆栈上存在太多的跨库混合。这就是为什么使用 TryEnter 进行基于超时的监视器获取对于预防死锁来说通常是一个坏主意。
正如您所读到的,似乎在资源受限的情况下,我们可能会从 lock(o) 内部使用的 Monitor 的 Enter 方法抛出异常。
那么也许您的解决方案类似于旋转等待?
{
uint iters = 0;
while (!cond) {
if ((++iters % 50) == 0) {
// Every so often we sleep with a 1ms timeout (see #30 for justification).
Thread.Sleep(1);
} else if (Environment.ProcessorCount == 1) {
// On a single-CPU machine we yield the thread.
Thread.Sleep(0);
} else {
// Issue YIELD instructions to let the other hardware thread move.
Thread.SpinWait(25);
}
}
}
cond 可能是一些
private volatile int cond = 0
与例如一起使用Interlocked.CompareExchange,您可以在其中更改为例如Thread.Current.ManagedThreadID 或其他非零值?