在异步代码中使用 BinaryWriter 或 BinaryReader

本文关键字:BinaryWriter BinaryReader 异步 代码 | 更新日期: 2023-09-27 18:34:37

我有一个要写入文件的float列表。下面的代码可以做到这一点,但它是同步的。

List<float> samples = GetSamples();
using (FileStream stream = File.OpenWrite("somefile.bin"))
using (BinaryWriter binaryWriter = new BinaryWriter(stream, Encoding.Default, true))
{
    foreach (var sample in samples)
    {
        binaryWriter.Write(sample);
    }
}

我想异步执行该操作,但BinaryWriter不支持异步操作,这是正常的,因为它每次只写入几个字节。但大多数时候该操作使用文件 I/O,我认为它可以而且应该是异步的。

我尝试用BinaryWriter写入MemoryStream,完成后,我将MemoryStream复制到带有CopyToAsyncFileStream,但是这导致大文件的性能下降(总时间(高达 100%。

如何将整个操作转换为异步操作?

在异步代码中使用 BinaryWriter 或 BinaryReader

无论如何,正常的写入操作通常最终都会异步完成。 OS 立即接受写入写入缓存,并在稍后的某个时间将其刷新到磁盘。 应用程序不会被实际磁盘写入阻止。

当然,如果您正在写入可移动驱动器,则通常会禁用写缓存,并且您的程序将被阻止。


我建议您通过一次传输一个大块来大大减少操作数量。 即:

  1. 分配所需块大小的new T[BlockSize]
  2. 分配new byte[BlockSize * sizeof (T)]
  3. 使用 List<T>.CopyTo(index, buffer, 0, buffer.Length) 将批从列表中复制出来。
  4. 使用Buffer.BlockCopy将数据放入byte[]
  5. 在单个操作中将byte[]写入流。
  6. 重复 3-5,直到到达列表末尾。 小心最后一批,这可能是部分块。

您的内存流方法是有意义的,只需确保批量写入,而不是等待内存流增长到文件的完整大小,然后一次写入所有内容。

像这样的东西应该可以正常工作:

var data = new float[10 * 1024];
var helperBuffer = new byte[4096];
using (var fs = File.Create(@"D:'Temp.bin"))
using (var ms = new MemoryStream(4096))
using (var bw = new BinaryWriter(ms))
{
  var iteration = 0;
  foreach (var sample in data)
  {
    bw.Write(sample);
    iteration++;
    if (iteration == 1024)
    {
      iteration = 0;
      ms.Position = 0;
      ms.Read(helperBuffer, 0, 1024 * 4);
      await fs.WriteAsync(helperBuffer, 0, 1024 * 4).ConfigureAwait(false);
    }
  }
}

这只是示例代码 - 确保正确处理错误等。

有时,这些帮助程序类毫无帮助。

试试这个:

List<float> samples = GetSamples();
using (FileStream stream = File.OpenWrite("somefile.bin"))
{
    foreach (var sample in samples)
    {
        await stream.WriteAsync(BitConverter.GetBytes(sample), 0, 4);
    }
}