Пытаюсь разобраться как написать процесс сжатия и разжатия при помощи 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);
}
}
}
}
}
Ваш код неявно предполагает, что GZipStream
'у можно скормить случайный фрагмент сжатого файла, и он его сможет разжать.
Это не так. В сжатом файле есть служебная информация, которую GZipStream
не может найти, если ему достаётся лишь кусок файла.
Судя по всему, случайные данные воспринимаются кодом как управляющая информация, что и приводит к проблемам.
Формат gzip состоит из нескольких, кладущихся впритык кусков. Какие бы ни были размеры каждого куска, архиватор распаковывает куски один за одним. В начале каждого куска есть десятибайтный заголовок, содержащий информацию о куске. Но в этом заголовке нет информации о том, какого размера упакованные данные. За заголовком следует информация, упакованная алгоритмом Deflate, после которой — контрольная сумма и размер распакованных данных.
Поэтому склеивать файл из кусков легко, просто можно укладывать их один за другим. А вот для того, чтобы разбить файл на правильные куски, требуется узнать границы куска. Я покамест не вижу, как сделать это, не осуществляя по сути распаковку Deflate.
Ваш код по сути берёт случайные границы кусков, поэтому интерпретировать данные на границе куска как длину нельзя.
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Как в ASPNET MVC ссылку вложить элемент, сохраняя при этом виртуальный путь?
При попытке создать обьект через контейнер Ninject вылетает exception : Object reference not set to an instance of an objectПодскажите, пожалуйста, в чем может быть проблема?
Есть объект, наследованный от IEnumerableStackOverflowException возникает в методе MoveNext этого класса, на строчке с Regex