2

有没有办法确定如果free()在某个内存块指针上调用是否会失败?

我有以下情况:有权访问共享资源的线程失败,而可能处于释放所述资源的状态。现在我需要设计一种安全的方法来清理这个共享资源。

当然,我已经为正常情况分配了资源的所有权,但是上述限制情况呢?

更新:如果我使用额外的同步机制,它只会做更多的清理工作,并且可能涉及额外的限制条件。如果可能,我想限制/避免这些。

解决方案:我最终决定执行重构。感谢所有贡献者。你们真棒!

4

4 回答 4

6

我见过各种各样的尝试,包括这个:

void m_free(void **p)
{
        if (*p != NULL) {
                free(*p);
                *p = NULL;
        }
}

不仅取消引用类型双关指针会破坏各种平台,“插入此示例”只有在您初始化、释放和重新初始化每个现有函数(包括编译的库)中的每个指针并使用 C 库时才能工作做同样的事情。

然后,处理优化和无锁线程安全问题。呸!

简而言之,如果您无法跟踪在单个函数中分配的内容,那么是时候重新考虑该函数了。做到这一点就够了……你会发现对更安全的 free() 的需求很快就消失了。如果在它支持的平台上工作, Valgrind是你的朋友。根据你的标签,这真的是你的朋友:)

或者,使用malloc()自费进行垃圾收集,具体取决于您如何分配事物并完全摆脱 free()。在那之后,调试几乎变得非常有趣。

希望你要重新考虑因素?虽然您似乎(也)遇到了互斥问题,但它只会导致重新分解。即,让 free() 之前的行阻塞,或者在尝试获取锁时失败.. 并在拥有锁的线程中将释放的指针设置为 NULL,至少在您实现的结构中。

于 2009-11-07T15:21:16.927 回答
2

我不相信有一个符合您要求的接口。

不过,我可以想到一些技巧。您可以让容易失败的线程调用包装器free()而不是free()直接调用;包装器可以保存地址或最后几个地址,以便您可以确定块是否已释放。您还可以阻止信号、建立临界区或处理任何其他可能中断 *thread 的事情。

更新:垂死的线程在退出/清理之前是否释放过这个内存?我经常为不需要在稳定状态下释放的内存编写 malloc 前端(作为速度优化)。如果线程只是设置自己,您可以在线程启动之前 malloc 内存并让线程调用前端,该前端仅分发未链接的、不可释放的动态块。它将运行得更快,然后当线程死亡时,您可以一次释放整个块。这里的一般想法是通过调用可以在事后清理的服务来让容易发生故障的线程获取其内存。

还有一个想法:每个线程堆呢?如果可以说服线程从它们自己的堆中分配仅在其生命周期内需要的内存,那么当线程重新加入父线程时,这将很好地组织清理任务以释放整个线程堆。

于 2009-11-07T15:06:17.630 回答
1

我认为没有办法完全按照你的要求去做。

问题是没有办法确定垂死的线程在死时处于什么状态。它只是简单地调用了 free() 并且没有进一步吗?free() 是否将块添加回空闲列表?你说不出来。


如果线程以这种方式死亡是非常罕见的情况(因此可以将未释放的内存留在周围 - 你只想知道不要使用它)然后以下(使用 Windows 调用)释放内存并 '标记' 它对您的其他线程免费:

void* ptr;
...
void* desiredPtr = ptr;
if(InterlockedCompareExchangePointer(&ptr, NULL, desiredPtr) == desiredPtr)
  free(desiredPtr);

它的作用是确保只有一个线程正在尝试释放内存,并且在它执行之前将地址设置为 NULL,因此没有其他线程会尝试释放()它。


如果偶尔保留内存是不可接受的,那么最好的方法可能是拥有一个单独的线程,其唯一的工作是释放内存。然后其他线程可以将空闲内存线程的空闲请求排队。由于 free-memory-thread 非常简单,它永远不会死,并且可以正确完成 free 操作。

于 2009-11-07T16:31:49.113 回答
0

如果您free使用有效指针进行调用,我看不出它会如何失败。如果它失败了,它一定是由于一个无效的指针。

除了同步对共享内存的访问(例如,使用互斥体)之外,所有权也必须明确以避免双重释放等情况。双重释放是指两个或多个线程具有有效指针,但随后有多个线程尝试释放内存。尽管第二个线程有一个非空指针,但它不再有效。

如果您在 C/C++ 中遇到内存问题,您可以尝试像HeapAgent这样的内存库。像这样的内存库将检测和初始化每个内存分配。在释放内存之前,它首先检查内存指针是否有效并且没有缓冲区溢出错误。应该没有代码更改,因为它可以简单地替换内置的 malloc/free。此外,该库可以帮助查找内存泄漏、覆盖和无效引用。

解决您的问题的另一种策略可能是将资源清理集中到一个线程。当一个线程处理完资源后,它只是将其标记为可供垃圾收集器线程清理。

当然,还有 C 计划——使用 Java……开玩笑。

于 2009-11-07T19:00:27.237 回答