如何保证在多线程中写入文件有异常?
How to guaranteed write into file in multithreading with exceptions?
这是一个简化的例子
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
new Thread(() => Method1()).Start();
new Thread(() => Method2()).Start();
Console.Read();
}
private static void Method1()
{
using (StreamWriter sw = new StreamWriter(@"h:\data.txt"))
{
int i = 100000000;
while (true)
{
Thread.Sleep(100);
sw.WriteLine(i);
i++;
}
}
}
private static void Method2()
{
Thread.Sleep(6000);
throw null;
}
}
}
如果异常过早发生在另一个线程中,StreamWriter 不会将数据写入文件。发生异常时文件 data.txt 为空。
我试了一下这种情况并找到了一些解决方法:
如果我增加异常线程的睡眠间隔(并减少写入文件的间隔),文件将充满数据。这不是一个选择,因为我不知道什么时候会出现异常。
由于之前的解决方法,我可以减小流写入器的缓冲区大小。但是如果我把它设置得太小,它似乎不起作用——例如,这段代码
FileStream fs = new FileStream(@"h:\data.txt", FileMode.Create);
使用 (StreamWriter sw = new StreamWriter(fs, Encoding.Default, 10))
不起作用,因为第一次写入操作仅在缓冲区中等待写入文件的大约 385 个整数时发生。
如果我在异常发生前关闭写入器,文件将被填充。但这不是一个好的选择——我必须每秒写入文件 1 到 10 次。如此频繁地打开和关闭写入器不是一个好主意,是吗?
我可以像这样捕获异常
private static void Method2()
{
try
{
Thread.Sleep(6000);
throw null;
}
catch
{
Console.WriteLine("Exception!");
}
}
一切都会好起来的——应用程序不会终止,文件会逐包填充。但事实并非如此——我无法控制异常发生的时间和地点。我尝试在所有地方使用 try-catch 但我可能会遗漏一些东西。
所以情况是:StreamWriter的buffer没有满,另外一个线程发生exception,没有被catch,所以应用会被终止。如何不丢失这些数据并将其写入文件?
据我了解您的情况,您假设某处存在错误并且进程可能随时终止。您想尽可能多地保存数据。
您应该可以在 StreamWriter
上呼叫 Flush
。这会将数据推送到 OS。如果您的进程终止,数据最终将由 OS.
写入
如果你不能说服 StreamWriter
由于某种原因实际冲洗你可以使用 FileStream
并写入它(伪代码:fileStream.Write(Encoding.GetBytes(myString))
)。然后您可以刷新 FileStream
或使用缓冲区大小 1.
当然最好在一开始就阻止进程被终止。这通常直接使用 Task
而不是使用原始 Thread
s.
Flushing 流将确保其所有内容都被推送到其基础文件中。
这将确保在您完成操作后保存所有数据,并且后续异常不会使您的应用程序丢失数据。
这是一个简化的例子
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
new Thread(() => Method1()).Start();
new Thread(() => Method2()).Start();
Console.Read();
}
private static void Method1()
{
using (StreamWriter sw = new StreamWriter(@"h:\data.txt"))
{
int i = 100000000;
while (true)
{
Thread.Sleep(100);
sw.WriteLine(i);
i++;
}
}
}
private static void Method2()
{
Thread.Sleep(6000);
throw null;
}
}
}
如果异常过早发生在另一个线程中,StreamWriter 不会将数据写入文件。发生异常时文件 data.txt 为空。
我试了一下这种情况并找到了一些解决方法:
如果我增加异常线程的睡眠间隔(并减少写入文件的间隔),文件将充满数据。这不是一个选择,因为我不知道什么时候会出现异常。
由于之前的解决方法,我可以减小流写入器的缓冲区大小。但是如果我把它设置得太小,它似乎不起作用——例如,这段代码
FileStream fs = new FileStream(@"h:\data.txt", FileMode.Create);
使用 (StreamWriter sw = new StreamWriter(fs, Encoding.Default, 10))
不起作用,因为第一次写入操作仅在缓冲区中等待写入文件的大约 385 个整数时发生。
如果我在异常发生前关闭写入器,文件将被填充。但这不是一个好的选择——我必须每秒写入文件 1 到 10 次。如此频繁地打开和关闭写入器不是一个好主意,是吗?
我可以像这样捕获异常
private static void Method2() {
try { Thread.Sleep(6000); throw null; } catch { Console.WriteLine("Exception!"); }
}
一切都会好起来的——应用程序不会终止,文件会逐包填充。但事实并非如此——我无法控制异常发生的时间和地点。我尝试在所有地方使用 try-catch 但我可能会遗漏一些东西。
所以情况是:StreamWriter的buffer没有满,另外一个线程发生exception,没有被catch,所以应用会被终止。如何不丢失这些数据并将其写入文件?
据我了解您的情况,您假设某处存在错误并且进程可能随时终止。您想尽可能多地保存数据。
您应该可以在 StreamWriter
上呼叫 Flush
。这会将数据推送到 OS。如果您的进程终止,数据最终将由 OS.
如果你不能说服 StreamWriter
由于某种原因实际冲洗你可以使用 FileStream
并写入它(伪代码:fileStream.Write(Encoding.GetBytes(myString))
)。然后您可以刷新 FileStream
或使用缓冲区大小 1.
当然最好在一开始就阻止进程被终止。这通常直接使用 Task
而不是使用原始 Thread
s.
Flushing 流将确保其所有内容都被推送到其基础文件中。
这将确保在您完成操作后保存所有数据,并且后续异常不会使您的应用程序丢失数据。