我想异步读取和写入字节和结构化值类型,而不必担心解码器和字节移位:有什么东西可以让我这样做吗?
3 回答
BinaryReader
使用or是不可能的BinaryWriter
。您可以同时从底层读取BaseStream
,但文档说明如下:
在读取或使用 BinaryReader 时使用底层流可能会导致数据丢失和损坏。例如,可能会多次读取相同的字节,可能会跳过字节,或者字符读取可能变得不可预测。
因此,唯一的方法是滚动您自己的实现。但这样做的好处是有争议的。来自 Microsoft 的 Marco Greg 在博客文章我应该为同步方法公开异步包装器吗?:
Jon:BinaryReader/Writer 没有 XxxAsync 方法的原因是这些类型的方法通常只从先前打开的基础流中读取/写入很少的字节。在实践中,数据经常被缓存,从底层源获取数据所需的时间通常非常短,因此不值得异步执行。
值得注意的是,这些类型的某些方法在某些情况下可能会传输大量数据(例如 ReadString)。更进一步,这些方法的异步版本可能会添加也可能不会添加,但不太可能在不久的将来发生。
一般而言,如果您正在读取的数据量很大(至少数百或数千字节),或者您是第一次访问资源(例如,第一次从文件中读取),您应该只考虑异步 IO 方法即使您正在读取一个字节,也可能需要启动磁盘)。
这听起来很合理。如果您确实需要解决方案,除了滚动您自己的BinaryReader
/之外,还有几种解决方法BinaryWriter
。您可以在单独的线程中运行它(这可能效率低下),或者如果您愿意更改文件格式或有线协议,您可以使用此模式(伪代码):
//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))
{
//...
}
请注意,此模式仅在完整缓冲区很容易放入内存并且性能可能会受到影响时才有用。
您可以使用FileStream
,确保正确调用构造函数,以便它启用异步并具有足够大的缓冲区。
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);
现在您有了一个字节数组,您可以使用三个选项将它们转换为对象:
- 利用
BitConverter
- 使用
BinaryFormatter
和MemoryStream
- 编写自己的自定义逻辑
我创建了这些类的异步版本:https ://github.com/ronnieoverby/AsyncBinaryReaderWriter
示例用法:
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);
}
BCL 二进制读取器/写入器类上存在的所有相同的读取/写入方法都存在。