【问题标题】:Can allocating memory from a private heap cause a deadlock?从私有堆分配内存会导致死锁吗?
【发布时间】:2014-01-16 08:46:47
【问题描述】:

我有两个线程。线程 1 定期挂起线程 2 以收集一些统计信息。线程 1 需要为这些统计信息分配内存,而另一个线程被挂起。由于挂起的线程可能持有堆锁,如果线程 1 从同一堆分配内存,则可能发生死锁。

可能的解决方案:为线程 1 使用私有堆以避免死锁。

如果必须增加私有堆的大小会怎样?必须再次存在某种全局锁,将内存页面分配到堆同步。所以在我的理解中,如果线程 2 在挂起期间持有这个全局锁,仍然有可能导致死锁。这是正确的还是使用一些特殊的“无锁原子”机制完成全局内存管理?

编辑:

线程 2 可能被 CLR 垃圾收集器或我自己调用 SuspendThread 挂起。通过调用HeapCreate 创建一个私有堆。

【问题讨论】:

  • 如何挂起线程?
  • 请查看已编辑的问题。
  • 这就是为什么 SuspendThread() 是邪恶的并且在 .NET Framework 中具有 [Obsolete] 属性的原因。不要使用它。
  • 我正在编写一个小型 .NET 分析器,我必须(!)挂起线程。我使用的是 windows api SuspendThread 函数,而不是托管函数。

标签: windows memory-management


【解决方案1】:

正如 Hans Passant 和 David Heffernan 已经指出的那样,SuspendThread 对众所周知的问题很麻烦,如果有人可以帮助它,那么永远不会暂停线程,而是让它阻塞在同步原语上。这样,您就可以提前知道何时可以阻塞,何时不能阻塞,而这不会处于内存分配的中间。

显然这对您的应用程序来说不太可能。

指定每个堆由单个锁保护,以防止多线程访问。因此,按照您的设想使用私有堆听起来像是一个可行的解决方案。 HeapCreate 为指定的最大大小保留地址空间,并为初始大小提交内存。此后它并没有真正“增长”,而只是在已经保留的地址空间中提交更多内存。
虽然没有具体说明堆必须如何操作(所以我们只能猜测),但这不是应该能够死锁的东西,因为它可能只是对VirtualAlloc 的调用(或者取决于它的实现方式,它可能只能是保护页面上的页面错误,例如堆栈以这种方式提交其内存)。

VirtualAlloc 或虚拟内存/内存映射子系统不能有一个“更大的锁”(嗯,他们肯定有,但不是用户进程拥有的!)因为如果他们有,您将能够轻松地在计算机上运行拒绝服务攻击,从而使 计算机上的所有其他进程陷入死锁。页面错误一直在发生,因此一旦计算机上的任何进程暂停线程,任何进程(即使是那些不调用VirtualAlloc的进程)都不会是安全的。 幸运的是,事实并非如此(这将是一场噩梦!)。

到目前为止,您使用私有堆的解决方案可能是一个不错的解决方案。

作为一种替代解决方案,由于您正在编写分析器并且需要为“统计”分配内存(CPU 使用、上下文切换、工作集大小、那种东西?),人们可能会认为这或多或少一个恒定的内存量,或者至少一个具有可预测上限的内存量。
因此,您可以暂停其他线程之前简单地分配内存。这意味着任何关于锁的问题都无关紧要。

【讨论】:

    【解决方案2】:

    您的假设条件如下:

    • 线程 2 持有锁。
    • 线程 2 被挂起。
    • 线程 1 尝试获取锁。
    • 线程 2 只能由线程 1 恢复。

    当所有这些条件都成立时,你就有了死锁。

    【讨论】:

      猜你喜欢
      • 2022-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多