StreamWriterBufferedDataLost
当对 StreamWriter 执行写入,但是随后未在销毁 StreamWriter 的实例之前调用 Flush 或 Close 方法时,将激活 streamWriterBufferedDataLost 托管调试助手 (MDA)。当启用此 MDA 时,运行库将确定 StreamWriter 中是否仍然存在任何缓冲数据。如果不存在缓冲数据,则激活该 MDA。调用 Collect 和 WaitForPendingFinalizers 方法可以强制终结器运行。否则终结器似乎将在任意时间运行,并且可能在进程退出时根本就不运行。在启用此 MDA 的情况下显式运行终结器将有助于更可靠地重现此类问题。
症状
StreamWriter 未将最后 1 至 4 KB 数据写到文件。
原因
StreamWriter 在内部缓冲数据,这需要调用 Close 或 Flush 方法将缓冲数据写到基础数据存储区。如果没有适当地调用 Close 或 Flush,StreamWriter 实例中缓冲的数据可能不会按预期写出。
下面是此 MDA 应该捕获的编写得很糟糕的代码的示例。
复制代码
// Poorly written code.
void Write()
{
StreamWriter sw = new StreamWriter("file.txt");
sw.WriteLine("Data");
// Problem: forgot to close the StreamWriter.
}
如果触发垃圾回收,然后将其挂起直至终结器完成,则上面的代码将更可靠地激活此 MDA。若要跟踪此类问题,可以在调试版本中将下面的代码添加到上面的方法的结尾处。这样将有助于可靠地激活 MDA,不过它并未解决此类问题的根源。
复制代码
GC.Collect();
GC.WaitForPendingFinalizers();
解决方案
在关闭具有 StreamWriter 的实例的应用程序或任何代码块之前,确保调用 StreamWriter 的 Close 或 Flush。达到此目的的最佳机制之一是用 C# using 块(在 Visual Basic 中为 Using)创建该实例,这样将确保调用编写器的 Dispose 方法,从而正确关闭该实例。
复制代码
using(StreamWriter sw = new StreamWriter("file.txt"))
{
sw.WriteLine("Data");
}
下面的代码演示能达到相同效果的解决方案,不过它使用的是 try/finally 而不是 using。
复制代码
StreamWriter sw;
try
{
sw = new StreamWriter("file.txt"));
sw.WriteLine("Data");
}
finally
{
if (sw != null)
sw.Close();
}
如果不能使用这其中任一种方案(例如,如果 StreamWriter 存储在静态变量中,并且在其生存期结束时很难运行代码),那么在最后使用 StreamWriter 之后调用其上的 Flush,或者在第一次使用它之前将 AutoFlush 属性设置为 true,应该会避免此问题。
复制代码
private static StreamWriter log;
// static class constructor.
static WriteToFile()
{
StreamWriter sw = new StreamWriter("log.txt");
sw.AutoFlush = true;
// Publish the StreamWriter for other threads.
log = sw;
}
对运行库的影响
此 MDA 对运行库没有影响。
输出
指示发生此冲突的消息。
配置
复制代码
<mdaConfig>
<assistants>
<streamWriterBufferedDataLost />
</assistants>
</mdaConfig>