I have the following test application that simulates my application scenario:
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;
}
It uses the following class as the 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();
}
}
The important things to note are:
I have a few questions, as I am quite new to the TPL dataflow library.
Produce()
, and the exception won't happen here... Is there a best practise for this pattern? Dispose is kind of broken because it does not wait until all activity is shut down. This is probably not what you want. See http://blog.stephencleary.com/2013/03/async-oop-6-disposal.html .
AsyncFileWriter has an unbounded buffer. If the file writer is overloaded this will consume more and more memory.
AsyncFileWriter seems OK otherwise.
But since the file system does its own buffering you maybe can get away with not using anything like AsyncFileWriter at all. Just writing to the file might be good enough.
Regarding exceptions: You can expose an event for that. Or, in case of an exception you shut down writing, remember the exception and make it possible for consumers of the class to examine the exception. Also, Produce must start to fail. Dispose should not fail but before disposing users of the class need to shut it down and check for exceptions.
So either use an event or move the class into an error state.
Probably, the error state is a little nicer for consumers because the exception is discovered at their leisure, not pushed through an event. Complete
should be public. The caller should await it and the exception should be thrown here.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.