Как смешать 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
,