I\'d like to read and write bytes and structured value types, asynchronously, without having to worry about decoders and byte-shifting: is there something out there that wou
It's not possible with BinaryReader
or BinaryWriter
. You could read concurrently from the underlying BaseStream
, but the documentation states the following:
Using the underlying stream while reading or while using the BinaryReader can cause data loss and corruption. For example, the same bytes might be read more than once, bytes might be skipped, or character reading might become unpredictable.
Therefore the only way is to roll your own implementation. But the benefit of doing that is debatable. Marco Greg from Microsoft added the following comment to the blog article Should I expose asynchronous wrappers for synchronous methods?:
Jon: The reason that the BinaryReader/Writer do not have XxxAsync methods is that the methods on those types typically read/write only very few bytes from an underlying stream that has been previously opened. In practice, the data is frequently cached and the time required to fetch the data from the underlying source is typically so small that it is not worth it doing it asynchronously.
Notably, there are some methods on these types that in some circumstances may transfer larger amounts of data (e.g. ReadString). Further down the line, Async versions for those methods may or may not be added, but it is unlikely it will happen in the immediate future.
In general, you should only consider Async IO methods if the amount of data you are reading is significant (at least several hundreds or thousands of bytes), or if you are accessing a resource for the first time (e.g. a first read from a file may require to spin up the disk even if you are reading one byte).
This sounds reasonable. If you do need a solution there are several workarounds apart from rolling your own BinaryReader
/BinaryWriter
. You can run it in a separate thread (which can be inefficient) or if you're willing to change the file format or wire protocol you could use this pattern (pseudo-code):
//read packet length
await stream.ReadAsync(buffer);
var packetLength=convertToInt(buffer);
//read complete packet asynchronously
await stream.ReadAsync(buffer,packetLength);
//process packet with BinaryReader
using(var br=new BinaryReader(new MemoryStream(buffer))
{
//...
}
Please note that this pattern is only useful if the complete buffer easily fits in memory and that performance might suffer.
You can use FileStream
, make sure you call the constructor properly so it enables async and has a large enough buffer.
var buffer = new byte[2048];
using var r = new FileStream(
Path,
FileMode.Open,
FileAccess.ReadWrite,
FileShare.None,
2048000,
FileOptions.Asynchronous);
await r.ReadAsync(buffer, 0, 2048);
await r.Writesync(buffer);
Now that you have a byte array you can have three options to convert them to objects:
BinaryFormatter
and MemoryStream
I've created versions of those classes that are async: https://github.com/ronnieoverby/AsyncBinaryReaderWriter
Example usage:
async Task Main()
{
using var ms = new MemoryStream();
using var writer = new AsyncBinaryWriter(ms);
await writer.WriteAsync("today is: ");
await writer.WriteAsync(DateTime.Today.Ticks);
await writer.FlushAsync();
ms.Position = 0;
using var reader = new AsyncBinaryReader(ms);
var preamble = await reader.ReadStringAsync();
var payload = new DateTime(await reader.ReadInt64Async());
Console.WriteLine(preamble + payload);
}
All of the same read/write methods present on the BCL binary reader/writer classes are present.