不确定异步处理文件的正确方法
Not sure is the correct way in handling file asynchronously
我正在使用此代码在磁盘上写入一组文件:
var savingTasks = games.Games.Select(t=>{
var path = Path.ChangeExtension(Path.Combine(savePath,Path.GetFileName(t.Url)),"pgn");
Log.Information($"trying to save game in:{path}");
var fs = new FileStream(path,FileMode.CreateNew,FileAccess.ReadWrite);
opened.Add(fs);
var sr = new StreamWriter(fs);
writers.Add(sr);
var tsk = sr.WriteAsync(t.Pgn);
return tsk;
});
try
{
await Task.WhenAll(savingTasks);
var flushing = writers.Select(u=>u.FlushAsync());
await Task.WhenAll(flushing);
}
catch(Exception e)
{
Log.Fatal($"Cannot write to file:{e}");
throw e;
}
finally
{
opened.ForEach(s => s.Close());
}
在某些步骤中,我不相信自己做得最好,即使代码工作得很好。
不能说服我的部分是我如何处理关闭:我在 Select
中创建了一组任务,但我必须跟踪打开的文件才能关闭它们(参见 finally ),以及类似的方式,我必须管理 StreamWriter
的集合(参见 writers
)。
这不能说服我,有没有更好的方法?
你把事情复杂化了。
你应该为你的 FileStream
和 StreamWriter
使用一个 using
块,它负责在处理它们时刷新/关闭。
通过等待 WriteAsync
而不是返回它生成的 Task
,将确保您的 FileStream
和 StreamWriter
不会被过早处理:
var savingTasks = games.Games
.Select(async t =>
{
var path = Path.ChangeExtension(Path.Combine(savePath,Path.GetFileName(t.Url)),"pgn");
Log.Information($"trying to save game in:{path}");
using (var fs = new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, bufferSize: 4096, useAsync: true))
using (var sr = new StreamWriter(fs))
{
await sr.WriteAsync(t.Pgn);
}
});
try
{
await Task.WhenAll(savingTasks);
}
catch (Exception e)
{
Log.Fatal($"Cannot write to file:{e}");
throw;
}
我会将 FlushAsync
移动到 finally
因为如果在执行任务期间发生异常跳转,它们将不会被清除。
此外,为了清洁,我建议使用一种方法完成所有操作,如下一种:
var savingTasks = games.Games.Select(t=>ExecuteGameMethod(t));
try
{
await Task.WhenAll(savingTasks);
}
catch(Exception e)
{
Log.Fatal($"Cannot write to file:{e}");
throw;
}
public async Task ExecuteGameMethod(Game game)
{
var path = Path.ChangeExtension(Path.Combine(savePath,Path.GetFileName(game.Url)),"pgn");
Log.Information($"trying to save game in:{path}");
using(var fs = new FileStream(path,FileMode.CreateNew,FileAccess.ReadWrite,bufferSize:4096, isAsync:true ))
using(var sr = new StreamWriter(fs))
{
await sr.WriteAsync(game.Pgn);
await sr.FlushAsync();
}
}
我正在使用此代码在磁盘上写入一组文件:
var savingTasks = games.Games.Select(t=>{
var path = Path.ChangeExtension(Path.Combine(savePath,Path.GetFileName(t.Url)),"pgn");
Log.Information($"trying to save game in:{path}");
var fs = new FileStream(path,FileMode.CreateNew,FileAccess.ReadWrite);
opened.Add(fs);
var sr = new StreamWriter(fs);
writers.Add(sr);
var tsk = sr.WriteAsync(t.Pgn);
return tsk;
});
try
{
await Task.WhenAll(savingTasks);
var flushing = writers.Select(u=>u.FlushAsync());
await Task.WhenAll(flushing);
}
catch(Exception e)
{
Log.Fatal($"Cannot write to file:{e}");
throw e;
}
finally
{
opened.ForEach(s => s.Close());
}
在某些步骤中,我不相信自己做得最好,即使代码工作得很好。
不能说服我的部分是我如何处理关闭:我在 Select
中创建了一组任务,但我必须跟踪打开的文件才能关闭它们(参见 finally ),以及类似的方式,我必须管理 StreamWriter
的集合(参见 writers
)。
这不能说服我,有没有更好的方法?
你把事情复杂化了。
你应该为你的 FileStream
和 StreamWriter
使用一个 using
块,它负责在处理它们时刷新/关闭。
通过等待 WriteAsync
而不是返回它生成的 Task
,将确保您的 FileStream
和 StreamWriter
不会被过早处理:
var savingTasks = games.Games
.Select(async t =>
{
var path = Path.ChangeExtension(Path.Combine(savePath,Path.GetFileName(t.Url)),"pgn");
Log.Information($"trying to save game in:{path}");
using (var fs = new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, bufferSize: 4096, useAsync: true))
using (var sr = new StreamWriter(fs))
{
await sr.WriteAsync(t.Pgn);
}
});
try
{
await Task.WhenAll(savingTasks);
}
catch (Exception e)
{
Log.Fatal($"Cannot write to file:{e}");
throw;
}
我会将 FlushAsync
移动到 finally
因为如果在执行任务期间发生异常跳转,它们将不会被清除。
此外,为了清洁,我建议使用一种方法完成所有操作,如下一种:
var savingTasks = games.Games.Select(t=>ExecuteGameMethod(t));
try
{
await Task.WhenAll(savingTasks);
}
catch(Exception e)
{
Log.Fatal($"Cannot write to file:{e}");
throw;
}
public async Task ExecuteGameMethod(Game game)
{
var path = Path.ChangeExtension(Path.Combine(savePath,Path.GetFileName(game.Url)),"pgn");
Log.Information($"trying to save game in:{path}");
using(var fs = new FileStream(path,FileMode.CreateNew,FileAccess.ReadWrite,bufferSize:4096, isAsync:true ))
using(var sr = new StreamWriter(fs))
{
await sr.WriteAsync(game.Pgn);
await sr.FlushAsync();
}
}