C# 在后台写入文件
给定一个写入文本文件的方法C# 在后台写入文件,c#,.net,io,C#,.net,Io,给定一个写入文本文件的方法 public void WriteToFile( ) { var file = "C:\\AsyncTest.txt"; var writer = File.Exists( file ) ? File.AppendText( file ) : File.CreateText( file ); writer.WriteLine( "A simulated entry" ); writer.Close(); } 我需要模拟一个场景,在这个
public void WriteToFile( ) {
var file = "C:\\AsyncTest.txt";
var writer = File.Exists( file ) ? File.AppendText( file ) : File.CreateText( file );
writer.WriteLine( "A simulated entry" );
writer.Close();
}
我需要模拟一个场景,在这个场景中,这个方法可以在一个循环中调用,可能要调用几十次,并且必须异步运行
因此,我尝试在这样一个新线程中调用该方法(writer是WriteToFile所在的类)
它只运行了一次,但引发了一个IO异常,即该文件正被另一个进程在后续迭代中使用。事实上,这很有道理,但我不知道如何解决这个问题
我试着像这样使用Join()
Thread thread = new Thread( writer.WriteToFile );
thread.Start( );
thread.Join();
但是这会锁定调用线程,直到所有连接的线程都完成,哪种方式会破坏目的,不是吗
我尝试使用ThreadPool.QueueUserWorkItem(writer.WriteToFile)代码>,但得到相同的IO异常
我试着用锁
private object locker = new object();
public void WriteToFile( ) {
lock(locker){
//same code as above
}
}
但这没有明显的效果
我还尝试使用Task类,但没有成功
那么,如何在不锁定调用线程的情况下,将这些后台线程“叠加”到单个文件中而不发生冲突呢?您可以像尝试过的那样处理锁定,但需要使用语句包含:
private readonly object _lockObj = new Object();
public void WriteToFile( )
{
var file = "C:\\AsyncTest.txt";
lock (_lockObj)
{
using (StreamWriter writer = File.AppendText( file ))
{
writer.WriteLine( "A simulated entry" );
}
}
}
此外,您不需要利用CreateText
,因为AppendText
将创建不存在的文件。最后,我以前也遇到过这段代码的问题,因为锁将在Windows释放资源之前释放。这种情况很少见,但确实会发生,所以我只添加了一点重试逻辑来查找特定的异常。您可以使用类似以下内容:
// To enqueue the write
ThreadPool.QueueUserWorkItem(WriteToFile, "A simulated entry");
// the lock
private static object writeLock = new object();
public static void WriteToFile( object msg ) {
lock (writeLock) {
var file = "C:\\AsyncTest.txt";
// using (var writer = File.Exists( file ) ? File.AppendText( file ) : File.CreateText( file )) {
// As written http://msdn.microsoft.com/it-it/library/system.io.file.appendtext(v=vs.80).aspx , File.AppendText will create the
// file if it doesn't exist
using (var writer = File.AppendText( file )) {
writer.WriteLine( (string)msg );
}
}
}
请使用和文件一起使用
处理你的溪流
使用TPL或异步/等待功能
例如:
Task.Run(() => File.WriteAllText("c:\\temp\test.txt", "content"));
这将异步运行写操作,而无需您处理线程
此外,streams和stream Writer提供了WriteAsync方法,您可以使用这些方法并等待它们
更新
要避免“锁定”问题,请不要执行锁定:)
如果尝试从不同线程写入同一文件,则会发生锁定。您可以使用File.Open()方法并指定模式,以便它阻止线程并等待文件可写
但是阻塞是不好的。
所以我建议您,如果您想从多个线程写入,请创建一个队列并将您的写入任务放入该队列。您可以从多个线程安全地放置(使用ConcurrentQueue
)。
然后在后台任务中使用该队列,只需将队列中的内容逐个写入文件
就是这样:多个发布者,一个[文件写入]消费者,超级简单,不需要锁。另一个选项是创建队列。让主线程在队列上放置字符串,让一个持久性后台线程读取队列并写入文件。这真的很容易做到
private BlockingCollection<string> OutputQueue = new BlockingCollection<string>();
void SomeMethod()
{
var outputTask = Task.Factory.StartNew(() => WriteOutput(outputFilename),
TaskCreationOptions.LongRunning);
OutputQueue.Add("A simulated entry");
OutputQueue.Add("more stuff");
// when the program is done,
// set the queue as complete so the task can exit
OutputQueue.CompleteAdding();
// and wait for the task to finish
outputTask.Wait();
}
void WriteOutput(string fname)
{
using (var strm = File.AppendText(filename))
{
foreach (var s in OutputQueue.GetConsumingEnumerable())
{
strm.WriteLine(s);
// if you want to make sure it's written to disk immediately,
// call Flush. This will slow performance, however.
strm.Flush();
}
}
}
private BlockingCollection OutputQueue=new BlockingCollection();
void方法()
{
var outputTask=Task.Factory.StartNew(()=>WriteOutput(outputFilename),
TaskCreationOptions.LongRunning);
添加(“模拟条目”);
添加(“更多内容”);
//程序完成后,
//将队列设置为已完成,以便任务可以退出
OutputQueue.CompleteAdding();
//然后等待任务完成
outputTask.Wait();
}
void WriteOutput(字符串fname)
{
使用(var strm=File.AppendText(文件名))
{
foreach(OutputQueue.GetConsumingEnumerable()中的变量s)
{
标准写入线(s);
//如果要确保它立即写入磁盘,
//调用Flush。但是,这会降低性能。
strm.Flush();
}
}
}
后台线程在输出队列上执行非忙等待,因此它不使用CPU资源,除非它实际输出数据。由于其他线程只需在队列中放入一些内容,因此基本上不需要等待
有关更多信息,请参阅我的博客。尝试在全局(静态)变量的线程内锁定,并在锁定结束前(如100ms)添加一个轻微的睡眠(windows在打开和关闭文件方面存在问题)。一个文件还是多个文件?您想写的是固定的还是作为参数传递的?这是否有助于解锁文件?请使用任务。然后,您可以使用将下一个操作排队,依此类推。您打开文件的方式不能使其异步写入同一文件。为什么在此处使用volatile
?我本以为\u lockObj
应该是只读的。@ken,你说得对。老实说,我不确定。也许我还需要一杯咖啡。你没有解决他的锁定问题。你没有任何锁定问题,以防你处理流(并且.writealText在内部执行此操作)如果你不想在同一时间从不同的线程写入同一个文件,如果你想从不同的线程写入同一个文件,我建议不要锁定。将更新我的答案。对不起,我应该提到我正在使用.NET 4.0——我认为Task.Run是在4.5中引入的?@AlexeyRaga您可以使用File.Open()方法并指定模式,这样它将阻止线程并等待文件可写。
这我认为您无法做到(仅使用File.Open
)嘿,谢谢。这是可行的——至少在这个模拟中是如此——希望它能转化为实际的应用程序:)。但是,您是否介意解释一下,如果lock对象和write方法是静态的,为什么它可以工作,但是如果它们不是静态的,为什么它会失败(如解决方案的答案所示)?顺便说一下,不需要条件<代码>文件。如果文件不存在,则AppendText
将创建一个新文件。所以你可以使用(var writer=File.AppendText(File))编写,
你不知道吗。我的锁工作正常,我的团队认为锁坏了,想要排队……:)(很棒的博客,顺便说一下!)@四十二:很高兴你成功了。谢谢你的好话。A.
private BlockingCollection<string> OutputQueue = new BlockingCollection<string>();
void SomeMethod()
{
var outputTask = Task.Factory.StartNew(() => WriteOutput(outputFilename),
TaskCreationOptions.LongRunning);
OutputQueue.Add("A simulated entry");
OutputQueue.Add("more stuff");
// when the program is done,
// set the queue as complete so the task can exit
OutputQueue.CompleteAdding();
// and wait for the task to finish
outputTask.Wait();
}
void WriteOutput(string fname)
{
using (var strm = File.AppendText(filename))
{
foreach (var s in OutputQueue.GetConsumingEnumerable())
{
strm.WriteLine(s);
// if you want to make sure it's written to disk immediately,
// call Flush. This will slow performance, however.
strm.Flush();
}
}
}