【问题标题】:When can lock(syncObject) throw an exception?lock(syncObject) 什么时候可以抛出异常?
【发布时间】:2009-11-30 06:11:55
【问题描述】:

我在 .NET 中编写了一个 com 组件,如果我尝试在任何方法中锁定任何对象(由与我的 com 组件对话的非托管代码调用),我会得到一个异常。

目前我没有异常的确切文本,但也没有多大帮助。

所以我的问题是在什么情况下 lock(syncObject) 会抛出异常? 以下是一些事实:

  • syncObject 不为空
  • syncObject 尚未锁定

这是否与在 STA(单线程单元)或 MTA(多线程单元)中运行的被调用方有关?

【问题讨论】:

  • 什么例外?你能提供一个调用堆栈吗?
  • 您确实必须添加更多信息,并解释您究竟是如何“从非托管调用”的。

标签: .net multithreading synchronization locking com-interop


【解决方案1】:

来自this page

每次锁定获取都可能引发异常。做好准备。

如果锁获取遇到争用,大多数锁都会延迟分配事件,包括 CLR 监视器。在资源不足的情况下,此分配可能会失败,从而导致 OOM 源自锁的入口。 (请注意,典型的非阻塞自旋锁不会因 OOM 而失败,这允许它在某些资源受限的场景中使用,例如在 CER 内部。)类似地,像 SQL Server 这样的主机可以执行死锁检测,甚至通过以下方式打破这些死锁生成源自 Enter 语句的异常,表现为 System.Runtime.InteropServices.COMException。

对于此类异常,通常无能为力。但是必须可靠地处理故障的可靠性和安全性敏感代码应该考虑这种情况。我们本来希望主机死锁可以智能响应的情况,但是大多数库代码不能智能地展开到堆栈上的安全点,以便它可以回退并重试操作。典型堆栈上的跨库混合太多了。这就是为什么使用 TryEnter 进行基于超时的 Monitor 获取对于防止死锁来说通常是一个坏主意。

如您所见,似乎在资源受限的情况下,我们可能会从 Monitor 的 Enter 方法中抛出异常,而 lock(o) 在内部使用。

所以也许您的解决方案类似于旋转等待?

{
    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 或其他非零值?

【讨论】:

    猜你喜欢
    • 2018-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多