我有以下测试应用程序模拟我的应用程序场景:
class Program
{
static void Main()
{
Console.WriteLine("***Press R = record to new file, S = Stop Recording, E = Exit");
var timer = new Timer(Callback, null, 0, 1000);
while(!_Close) HandleInput(Console.ReadLine());
Console.WriteLine("Finished");
}
private static bool _Close;
private static void HandleInput(string input)
{
switch (input.ToLower())
{
case "r": CreateWriter();
break;
case "s": Console.WriteLine("File Closed: {0}", _FileWriter.Name);
_FileWriter.Dispose();
_FileWriter = null;
break;
case "e":
_Close = true;
break;
}
}
private static void CreateWriter()
{
if (_FileWriter != null)
_FileWriter.Dispose();
string filename = Path.Combine("C:\\", string.Format("{0:yyyy_MM_dd HH_mm_ss}.txt",DateTime.Now));
_FileWriter = new AsyncFileWriter(filename);
Console.WriteLine("New File Created: {0}", filename);
}
private static void Callback(object state)
{
if (_FileWriter != null)
_FileWriter.Produce(MakeData());
}
private static byte[] MakeData()
{
string data = string.Empty;
for (int i = 0; i < 50; i++)
{
data += string.Format("{0:yyyy-MM-dd HH:mm:ss.fff}{1}", DateTime.Now, Environment.NewLine);
}
return Encoding.UTF8.GetBytes(data);
}
private static AsyncFileWriter _FileWriter;
}
它使用以下类作为Consumer(/ producer):
public class AsyncFileWriter : IDisposable
{
private readonly FileStream _Filewriter;
private readonly Task _WriteTask;
private readonly BufferBlock<byte[]> _BufferBlock;
public AsyncFileWriter(string filename)
{
_Filewriter = new FileStream(filename, FileMode.CreateNew, FileAccess.Write, FileShare.None);
_BufferBlock = new BufferBlock<byte[]>();
_WriteTask = WriteToFile();
}
public void Produce(byte[] data)
{
_BufferBlock.Post(data);
}
public long Filesize { get; private set; }
public string Name { get { return _Filewriter.Name; } }
private async Task WriteToFile()
{
while (await _BufferBlock.OutputAvailableAsync())
{
byte[] data = _BufferBlock.Receive();
await _Filewriter.WriteAsync(data, 0, data.Length);
Filesize = _Filewriter.Length;
}
}
private async Task Complete()
{
_BufferBlock.Complete();
await Task.WhenAll(_WriteTask, _BufferBlock.Completion);
//now close the file
_Filewriter.Dispose();
}
public void Dispose()
{
Complete();
}
}
需要注意的重要事项是:
我有几个问题,因为我对TPL数据流库很陌生。
Produce()
,并且此处不会发生异常......这种模式是否有最佳实践?答案 0 :(得分:1)
Dispose有点破碎,因为它不会等到所有活动都关闭。这可能不是你想要的。请参阅http://blog.stephencleary.com/2013/03/async-oop-6-disposal.html。
AsyncFileWriter具有无界缓冲区。如果文件编写器被重载,这将消耗越来越多的内存。
AsyncFileWriter似乎没问题。
但是由于文件系统自己进行缓冲,你可能会完全不使用像AsyncFileWriter这样的东西。只需写入文件就足够了。
关于例外:您可以为此公开事件。或者,如果出现异常,则关闭写入,请记住异常并使类的使用者可以检查异常。此外,生产必须开始失败。处置不应该失败但在处理类的用户之前需要关闭它并检查异常。
因此要么使用事件,要么将类移动到错误状态。
对于消费者而言,错误状态可能稍微好一些,因为异常是在闲暇时被发现的,而不是通过事件推动的。 Complete
应公开。调用者应该等待它,并且应该抛出异常。