#c_sharp #gzip
Пытаюсь разобраться как написать процесс сжатия и разжатия при помощи GZip в многопоточной
среде.
При разжатии файла в многопоточной среде выдает постоянно следующие ошибки: StackOverFlow,
System.OutOfMemory.
В режиме отладки на строках:
blockLength = BitConverter.ToInt32(lengthBuffer, 4);
и
_dataSize = BitConverter.ToInt32(compressedData[i], blockLength - 4)
во втором потоке присваиваются космические переменные, которые и ведут к фатальным
ошибкам. Пробовал ставить 4 байта, всячески "игрался", пытаясь реализовать по-разному.
Итог один. Прошу помощи!. Код ниже.
public abstract class GZip
{
protected static bool _cancelled = false;
protected static bool _success = false;
protected string sourceFile, destinationFile;
protected static int _threads = Environment.ProcessorCount;
protected const int buffer_size = 1024 * 1024;
protected static int blockSize = 10000000;
protected static byte[][] lastBuffer = new byte[_threads][];
protected static byte[][] compressedData = new byte[_threads][];
public GZip(string input, string output)
{
this.sourceFile = input;
this.destinationFile = output;
}
public int CallBackResult()
{
if (!_cancelled && _success)
return 0;
return 1;
}
public void Cancel()
{
_cancelled = true;
}
public abstract void Launch();
}
class Decompressor : GZip
{
public Decompressor(string input, string output) : base(input, output)
{
}
public override void Launch()
{
Console.Write("Decompressing");
try
{
using (FileStream _compressedFile = new FileStream(sourceFile, FileMode.Open))
{
using (FileStream _decompressedFile = new FileStream(sourceFile.Remove(sourceFile.Length
- 3), FileMode.Append))
{
int blockLength;
int _dataSize;
byte[] lengthBuffer = new byte[8];
Thread[] tPool = new Thread[_threads];
while (_compressedFile.Position < _compressedFile.Length)
{
for (int i = 0; (i < _threads) && (_compressedFile.Position
< _compressedFile.Length); i++)
{
Console.Write(".");
_compressedFile.Read(lengthBuffer, 0, lengthBuffer.Length);
blockLength = BitConverter.ToInt32(lengthBuffer, 4);
compressedData[i] = new byte[blockLength];
lengthBuffer.CopyTo(compressedData[i], 0);
_compressedFile.Read(compressedData[i], 8, blockLength
- 8);
_dataSize = BitConverter.ToInt32(compressedData[i],
blockLength - 4);
lastBuffer[i] = new byte[_dataSize];
tPool[i] = new Thread(Decompress);
tPool[i].Start(i);
}
for (int i = 0; (i < _threads) && (tPool[i] != null);)
{
if (tPool[i].ThreadState == ThreadState.Stopped)
{
_decompressedFile.Write(lastBuffer[i], 0, lastBuffer[i].Length);
i++;
}
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Error is occured!\n Method: {0}\n Error description
{1}", ex.TargetSite, ex.Message);
_cancelled = true;
}
}
public static void Decompress(object i)
{
using (MemoryStream _memoryStream = new MemoryStream(compressedData[(int)i]))
{
using (GZipStream cs = new GZipStream(_memoryStream, CompressionMode.Decompress))
{
cs.Read(lastBuffer[(int)i], 0, lastBuffer[(int)i].Length);
}
}
}
}
}
Ответы
Ответ 1
Ваш код неявно предполагает, что GZipStream'у можно скормить случайный фрагмент сжатого файла, и он его сможет разжать. Это не так. В сжатом файле есть служебная информация, которую GZipStream не может найти, если ему достаётся лишь кусок файла. Судя по всему, случайные данные воспринимаются кодом как управляющая информация, что и приводит к проблемам. Формат gzip состоит из нескольких, кладущихся впритык кусков. Какие бы ни были размеры каждого куска, архиватор распаковывает куски один за одним. В начале каждого куска есть десятибайтный заголовок, содержащий информацию о куске. Но в этом заголовке нет информации о том, какого размера упакованные данные. За заголовком следует информация, упакованная алгоритмом Deflate, после которой — контрольная сумма и размер распакованных данных. Поэтому склеивать файл из кусков легко, просто можно укладывать их один за другим. А вот для того, чтобы разбить файл на правильные куски, требуется узнать границы куска. Я покамест не вижу, как сделать это, не осуществляя по сути распаковку Deflate. Ваш код по сути берёт случайные границы кусков, поэтому интерпретировать данные на границе куска как длину нельзя.Ответ 2
После долгих мучений я все же разобрался как это реализовать по-человечески. Посмотреть код можно в моем профиле на Git: https://github.com/Mikezar/GZip
Комментариев нет:
Отправить комментарий