[英]C# garbage collector seems to be closing my StreamWriter too early
我有一个单一的记录器类。 在它的析构函数中,我调用Close()打印日志的页脚,然后关闭StreamWriter。
public void Close()
{
WriteLogFileFooter();
_logFile.Flush();
_logFile.Close();
}
问题是当从程序中的其他地方调用System.Enviornment.Exit(1)时(我自己没有编写的部分),页脚从不打印,我的记录器会因尝试写入封闭流而引发异常。 我只能假设Exit命令导致我的StreamWriter在我的Singleton被破坏之前被关闭。 我尝试在我的StreamWriter上使用GC.SupressFinalize(),但这似乎没有帮助。
您违反了终结器的一条明确规则:
Finalize方法不应引用任何其他对象。
http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=VS.90).aspx
在应用程序退出时收集对象之前,收集引用的托管对象完全有可能被收集。
UPDATE
如果您需要在应用程序退出时清理托管资源,则可以挂接AppDomain的ProcessExit事件,而不是依赖于终结器执行的非确定性行为。
您应该使您的记录器实现IDisposable
,并在using
块中使用它。 这意味着它将被确定性地处置,而现在它是不确定性地被破坏。
错误的原因是您的流有时会在记录器之前关闭,因为Exit
基本上会破坏所有内容(不确定性)并退出。 您应该使用确定性模式( IDisposable
)来避免这种情况。
实际上,析构函数在C#中很少有用,原因在于它们是非确定性的。 它们仅用于释放非托管资源。
此外,实现IDisposable
可能会使使用单例不方便。 我个人认为最好创建一个在整个程序中使用并在最后处置的实例,而不是一个明确的单例。
正如其他人已经明确指出的那样,您不应该尝试从记录器类的终结器中访问您的_logFile
对象。 您不应该访问终结器中的任何其他对象,因为垃圾收集器可能已经将它们擦除了。
我认为你可以通过几个简单的步骤来避免你的问题:
摆脱你目前的终结者。
在每次写入后执行_logFile.Flush
,而不是等到记录器对象的生命周期结束时才可能已经太晚了。
刷新日志文件流对我来说经常是合法的,因为拥有日志的重点是使用它来查找和处理发生错误的情况。 如果您的进程突然因特殊情况而终止,您的日志应尽可能完整; 因此,经常刷新日志流缓冲区似乎是一件明智的事情。
使您的记录器实现IDisposable
( 此MSDN杂志文章将向您解释如何完成此操作)并从那里关闭您的日志文件流。
我有同样的问题,我的解决方案如下:
GC.SuppressFinalize
immediately. 在类的中创建FileStream
,立即使用GC.SuppressFinalize
。 这使您负责清理流 Dispose()
of the class 类的Dispose()
中的流 public class LogFileEventListener : IDisposable
{
private bool disposed = false;
private FileStream fileStream;
public LogFileEventListener(string path)
{
//Opens a new file stream to log file
this.fileStream = new FileStream(path, FileMode.Append, FileAccess.Write);
GC.SuppressFinalize(this.fileStream);
}
/// <summary>Finalize the listener</summary>
~LogFileEventListener() { this.Dispose(); }
/// <summary>Disposes the listener</summary>
public override void Dispose()
{
try
{
if (!this.disposed)
{
/* Do you stuff */
//Close the log file
if (this.fileStream != null)
{
this.fileStream.Close();
this.fileStream = null;
}
base.Dispose();
}
}
finally
{
this.disposed = true;
GC.SuppressFinalize(this);
}
}
}
很可能StreamWriter
在其他地方被关闭。 尝试在你的单例构造函数中创建一个额外的StreamWriter
,写几次(确认它正在工作),然后在调用close之前再次在析构函数中写入它(close也将刷新)。
如果以上工作,那么您将知道其他一些代码正在关闭您的日志。 如果它不起作用,那么你就会知道它是一个.NET的东西(可能与引用变量的方式/位置有关)。
根据文档 ,您应该能够通过将StreamWriter
放在基类中来解决此问题。 这当然不适合你,因为你的测试用例不是标准的终结,而是一个程序退出,这意味着.NET在它想要的时候做它想做的事情。 相反,你应该捕获退出事件,处理这个类,然后返回,以保证事情按正确的顺序处理。 您还应该检查StreamWriter
是否已在终结器中关闭,以防程序因错误而中止。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.