lock(syncObject) 什么时候可以抛出异常?

发布于 2024-08-12 16:43:27 字数 261 浏览 6 评论 0原文

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

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

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

  • 同步对象不为空
  • 同步对象尚未锁定

它与在 STA(单线程单元)或 MTA(多线程单元)中运行的被调用者有什么关系吗?

I have written a com component in .NET and if I try to take a lock on any object in any method (that is invoked by unmanaged code talking to my com component) I get an exception.

I do not have the exact text of the exception at this moment but it wasn't much helpful either.

So my question is under what circumstances a lock(syncObject) would throw an exception?
Here are some facts:

  • syncObject is not null
  • syncObject is not already locked

Would it have anything to do with callee running in STA (Single Threaded Apartment) or MTA (Multi Threaded Apartment)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

浮华 2024-08-19 16:43:27

来自此页面

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

如果锁获取遇到争用,大多数锁都会延迟分配事件,包括 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 或其他非零值?

From this page:

Every lock acquisition might throw an exception. Be prepared for it.

Most locks lazily allocate an event if a lock acquisition encounters contention, including CLR monitors. This allocation can fail during low resource conditions, causing OOMs originating from the entrance to the lock. (Note that a typical non-blocking spin lock cannot fail with OOM, which allows it to be used in some resource constrained scenarios such as inside a CER.) Similarly, a host like SQL Server can perform deadlock detection and even break those deadlocks by generating exceptions that originate from the Enter statement, manifesting as a System.Runtime.InteropServices.COMException.

Often there isn’t much that can be done in response to such an exception. But reliability- and security-sensitive code that must deal with failure robustly should consider this case. We would have liked it to be the case that host deadlocks can be responded to intelligently, but most library code can’t intelligently unwind to a safe point on the stack so that it can back-off and retry the operation. There is simply too much cross-library intermixing on a typical stack. This is why timeout-based Monitor acquisitions with TryEnter are typically a bad idea for deadlock prevention.

So as you can read, it seems that under resource constrained conditions we might get exceptions thrown from the Enter method of Monitor which lock(o) uses internally.

So perhaps your solution is something like a spin-wait?

{
    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);
        }
    }
}

Where cond could be some

private volatile int cond = 0

used with e.g. Interlocked.CompareExchange where you change to e.g. Thread.Current.ManagedThreadID or something else non-zero?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文