【问题标题】:Correct way to free TCriticalSection object in Delphi在 Delphi 中释放 TCriticalSection 对象的正确方法
【发布时间】:2021-05-19 09:42:38
【问题描述】:

这是释放在 Delphi 的初始化部分中创建的 TCriticalSection 对象的正确方法吗?

initialization
  FPoolingCS := TCriticalSection.Create;
finalization
  FPoolingCS.Acquire;
  FreeAndNil(FPoolingCS);

我应该在Free 之前调用Release 方法吗?

Acquire 方法会抛出一些我需要处理的异常吗?

【问题讨论】:

    标签: multithreading delphi


    【解决方案1】:

    这不是释放关键部分的正确方法,原因有很多。

    根据文档EnterCriticalSection function

    如果等待,此函数可以引发 EXCEPTION_POSSIBLE_DEADLOCK 临界区操作超时。超时间隔为 由以下注册表值指定: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\CriticalSectionTimeout。不要处理可能的死锁 例外;而是调试应用程序。

    如果一个关键部分在它仍然拥有的情况下被删除,那么它的状态 等待已删除临界区所有权的线程是 未定义。

    当进程退出时,如果调用 EnterCriticalSection 块,它将立即终止进程。这可能 导致不调用全局析构函数。

    在 Windows 平台上调用 FPoolingCS.Acquire 调用 EnterCriticalSection 函数。所以第一个问题,获取临界区是否会引发异常,答案是肯定的。

    同样根据文档,您不应该尝试处理此类异常,但您必须调试应用程序,因为问题的根源在于其他代码。

    但是,在任何平台上发布临界区实例之前,您不应该调用 Acquire 的最显着原因是,如果此时有一些其他线程正在做一些工作并且依赖于该临界区,则意味着您的关闭和清洗过程完全中断。换句话说,如果Acquire 解决了您的问题,那么真正的问题在另一座城堡中,而您还没有真正解决任何问题,您只是稍微改变了动态,这可能有效,也可能无效,具体取决于所涉及的所有其他代码。

    出于同样的原因,在Free 之前调用Release 将毫无意义。如果还有其他涉及的线程还在运行,它们可能会在Free执行之前获得锁。

    只需在关键部分调用Free,或者如果您喜欢使用FreeAndNil,如果您的关闭过程被破坏,最终将崩溃。请记住,线程问题并不能始终如一地重现,因此没有崩溃仍然不意味着您拥有完全没有错误的代码。

    【讨论】:

    • 非常感谢您提供非常详尽的回答
    • +1。理想情况下,如果有工作线程,它们应该提供一些机制来优雅地停止。应用程序的干净关闭将首先等待并确认所有未完成的线程已完成执行,然后再释放它们所依赖的共享对象(如临界区等)。
    猜你喜欢
    • 2013-03-21
    • 1970-01-01
    • 2011-03-04
    • 1970-01-01
    • 2011-12-13
    • 2011-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多