我有两个线程。线程 1 定期挂起线程 2 以收集一些统计信息。线程 1 需要在另一个线程挂起时为这些统计信息分配内存。由于挂起的线程可能持有堆锁,因此如果线程 1 从同一堆分配内存,可能会发生死锁。
可能的解决方案:为线程 1 使用私有(private)堆以避免死锁。
如果必须增加私有(private)堆的大小会发生什么?必须再次存在某种全局锁来同步内存页到堆的分配。因此,根据我的理解,如果线程 2 在挂起期间持有此全局锁,仍然有可能导致死锁。这是正确的还是全局内存管理是通过某种特殊的“无锁原子”机制完成的?
编辑:
线程 2 可能会被 CLR 垃圾收集器或调用 SuspendThread 挂起。靠我自己。通过调用 HeapCreate 创建私有(private)堆.
最佳答案
正如 Hans Passant 和 David Heffernan 已经指出的那样,SuspendThread
对于众所周知的问题来说很麻烦,如果有人可以帮助它,那么永远就不会暂停线程,但让它阻塞在同步原语上。这样,您就可以提前知道何时可以阻塞、何时不能阻塞,并且这不会发生在内存分配过程中。
显然这对于您的应用程序来说不太可能。
指定每个堆都受到单个锁的保护,以防止多线程访问。因此,按照您的设想使用私有(private)堆听起来是一个可行的解决方案。 HeapCreate
保留指定最大大小的地址空间并提交初始大小的内存。此后它并没有真正“增长”,而是仅在已经保留的地址空间中提交更多内存。
虽然没有指定堆必须如何运行(所以我们只能猜测),但这不会导致死锁,因为它可能只是对 VirtualAlloc 的调用(或者取决于如何操作)它已实现,它可能只是保护页上的页面错误,例如堆栈以这种方式提交其内存)。
VirtualAlloc
或虚拟内存/内存映射子系统不能拥有“更大的锁”(嗯,它们当然,但不是由用户进程拥有的锁!)因为如果他们这样做,您将能够轻松地在计算机上运行拒绝服务攻击,从而使计算机上的所有其他进程陷入死锁。页面错误一直在发生,因此一旦计算机上的任何进程挂起线程,任何进程(即使是那些没有调用 VirtualAlloc 的进程)都不会是安全的。
幸运的是,事实并非如此(这将是一场噩梦!)。
到目前为止,您使用私有(private)堆的解决方案可能是一个不错的解决方案。
作为一种替代解决方案,由于您正在编写一个探查器并且需要为“统计”分配内存(CPU 使用、上下文切换、工作集大小,诸如此类的东西?),人们可能会认为这或多或少恒定的内存量,或至少具有可预测上限的内存量。
因此,您可以在挂起其他线程之前简单地分配内存。这意味着有关锁的任何问题并不重要。
关于windows - 从私有(private)堆分配内存会导致死锁吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21157044/