Как смешать Pinvoke WriteFile и BinaryFormatter с использованием одного и того же FileStream?

Я хочу использовать WriteFile для записи большого (~500 МБ) многомерного массива в файл (потому что BinaryFormatter очень медленно пишет большие вещи, и в.Net Framework нет другого способа записать многомерные байтовые массивы, только одиночные байты или одномерные массивы и выполнение цикла и запись байта за байтом происходит медленно).

Однако, оказывается, это запрещено:

IOException
The OS handle's position is not what FileStream expected. Do not use a handle simultaneously in one FileStream and in Win32 code or another FileStream. This may cause data loss.

Есть ли способ обойти это, кроме повторного открытия потока файлов каждый раз, когда я хочу писать с использованием BinaryFormatter после того, как я написал с использованием WriteFile?

1 ответ

(Я понимаю, что этот вопрос был оставлен и скоро будет удален.)

Во-первых, WriteFile а также BinaryFormatter просто не смешивайся WriteFile Предполагается, что вы знаете формат файла, то есть интерпретацию байтов, которые записаны в файл. BinaryFormatter это сериализатор, основанный на формате файла, который является внутренним для реализации Microsoft .NET (некоторые скажут, что проприетарные, хотя информацию можно найти в Интернете). Как следствие, вы даже не можете передать файл, сериализованный BinaryFormatter между Microsoft .NET и Mono C#.

Исходя из описания OP, ясно, что OP не должен был использовать BinaryFormatter на первом месте. В противном случае OP будет нести единоличную ответственность за потерю (невосстановимость) таких данных.

Как прокомментировал Ганс Пассант, FileStream.Write должен соответствовать вызову Win32 WriteFile Асимптотически. Это означает, что временные накладные расходы для каждого вызова могут быть смоделированы как alpha * numberOfBytesWritten + beta, где beta это чистые постоянные накладные расходы на вызов. Можно сделать эти издержки относительно незначительными, увеличив число байтов, записанных за вызов.

Учитывая, что мы не можем напрямую передать многомерный массив C# в WriteFile Вот предложение. На основании комментария ОП предполагается, что многомерный массив будет иметь размер byte[1024, 1024, 1024],

  • Во-первых, выделите временный одномерный массив достаточного размера. Типичные рекомендации варьируются от 4 КБ до нескольких МБ, но это только детали оптимизации. Для этого примера мы используем 1MB = byte[1048576] потому что он хорошо делит общий размер массива.

  • Затем мы записываем цикл for верхнего уровня по внешнему измерению.

  • На следующем шаге мы используем System.Array.Copy функция полезности для копирования 1024 x 1024 байты из двух самых внутренних измерений во временный одномерный массив. Это опирается на спецификацию C# для многомерных массивов, как описано в System.Array.Copy функция:

При копировании между многомерными массивами массив ведет себя как длинный одномерный массив, в котором строки (или столбцы) концептуально располагаются сквозными.

  • После копирования во временный одномерный массив его можно записать в FileStream.Write,
Другие вопросы по тегам