【问题标题】:Should disposable objects be disposed before a Windows Service stops?是否应在 Windows 服务停止之前处置一次性物品?
【发布时间】:2018-03-21 21:13:06
【问题描述】:

this 线程,Dispose() 必须显式为一次性对象调用。

Windows 服务停止时会发生什么?这些对象使用的资源是否自动释放?还是应该在服务停止之前将它们处理掉,如下所示:

public void Stop()
{
    cancellationTokenSource.Cancel();
    waitForAllTasksToExit();
    cancellationTokenSource.Dispose();
}

“处置是个好习惯”“服务停止时应该释放它们”我也是这么想的。但是参考文档有具体答案吗?

【问题讨论】:

  • AFAIK,当程序结束时,它的所有内存都会被释放。你有什么不一样的经历吗?
  • @CamiloTerevinto 这不仅仅是关于内存 - 打开(数据库)连接、文件访问等也在 Dispose 中处理
  • @CamiloTerevinto 好吧,现在没有问题了。但问题是,正确的方法是什么?是否应该明确完成?文档是这样说的吗?
  • 仅供参考(不是答案),但 using 块将自动调用 Dispose()
  • 进程“本地”的所有资源(内存、窗口句柄等)都将被自动释放。但是无论如何处置所有对象都是一个好习惯:正如@Toshi 所说,您必须考虑以某种方式在您处理之外的对象。例如,数据库服务器将认为未释放的连接仍处于活动状态(并最终将使用自己的方式释放它以检测该连接不再处于活动状态)

标签: c# dispose


【解决方案1】:

作为最佳实践,Dispose() 必须在一次性资源上显式调用。但是,当您停止 Windows 服务时,它会向所有底层进程(即您的 .NET 代码)发送控制消息,并停止或卸载所有 AppDomain。因此,所有使用的资源和内存都应该基本上被释放

【讨论】:

  • 只有在调用 Dispose 时才会释放的非托管资源呢?
  • @UweKeim 那不也被触发了吗?
  • @Rahul 未释放的非托管资源将在您重新启动操作系统时释放
  • @Rahul:非托管资源不仅是内存,还可以是数据库连接、文件流、网络连接。它们不会被释放,并且可能会锁定文件或导致数据库或网络出现问题。建议重新启动您的操作系统听起来很奇怪。某些服务器运行多年而无需重新启动,不建议将其作为正确处置模式的替代方案。
  • @Rahul:您展示了一个非常具体的 C 问题,并且 OP 询问这是否会导致内存泄漏(请注意,您无法将 C 与 C# 进行比较),您为什么认为这是其他所有非托管资源都一样吗?
【解决方案2】:

这取决于“这些对象使用的资源”是什么。 Dispose 方法本身不会在进程退出时被调用,但是除了Dispose 之外,大多数包含“非托管”资源的对象也有终结器。终结器将在进程退出时被调用,但它可能不会在进程崩溃时被调用。现在,在进程退出(或崩溃)the following 发生:

进程分配的所有资源都被释放。

所有内核对象都已关闭。

内核对象例如是文件句柄、套接字等。因此,即使进程崩溃并且终结器未运行(或根本没有终结器) - 文件或数据库\网络连接之类的东西仍将被操作系统关闭。

您可能对非托管资源有更广泛的定义。非托管意味着不受 .NET Framework 垃圾收集器管理。例如,如果我在创建某个对象时在磁盘上创建一个文件,并在处理该对象时删除该文件 - 您可能会说这也是“非托管”资源。操作系统不知道此类资源,如果我没有实现终结器或由于进程崩溃而未调用终结器,它将不会被“清理”。

总而言之-如果对象实现IDisposable-甚至在进程退出之前就将其处置。您可能不知道该对象开发者的意图,无论它是否具有终结器 - 因此最好在进程退出之前始终显式处置它。

【讨论】:

    【解决方案3】:

    @Evk 已经给出了答案,但对我来说并不完全清楚。在对文档进行广泛搜索后,我参考文档编译了以下答案。

    长答案:

    当服务停止时,垃圾收集器将释放它的资源。

    那么实现IDisposable 的对象呢? unmanaged resources 会被释放吗? 。来自Dispose Pattern

    GC 并不是专门为管理此类非托管资源而设计的,这意味着管理非托管资源的责任掌握在开发人员手中。

    那么,非托管资源会发生什么?他们永远不会被释放吗?

    还有机会

    Object 声明了一个虚方法 Finalize(也称为终结器),该方法在 GC 回收对象的内存之前由 GC 调用,并且可以被覆盖以释放非托管资源。

    不过这有一些drawbacks:

    1. 对象的终结器在 GC 检测到它符合收集条件后的某个未确定的时间段后被调用。
    2. 终结器在回收之间运行,因此对象的内存在下一轮垃圾回收之前不会释放。

    虽然documentation 表示实现IDisposable.Dispose 的对象应该覆盖Finalize 方法或将托管对象包装在SafeHandle 中,这样如果消费者忘记调用Dispose,仍会释放非托管资源;我们仍然可能会遇到麻烦。

    From the DocsFinalize 方法只有在派生类型覆盖它时才会被调用。

    那么,如果开发者没有实现上述 2(FinalizeSafeHandle)怎么办?那么我们就有问题了,没有人去释放unmanged的资源(至少文档没有说)。

    TLDR

    资源可能会或可能不会被释放(取决于上述情况)。因此,在您的服务的 Stop 方法中处理所有一次性对象(尚未处理)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-20
      • 1970-01-01
      • 2011-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多