1 / 1 / 0
Регистрация: 24.05.2015
Сообщений: 45
1

Многопоточное сжатие файла

27.07.2017, 17:01. Показов 11921. Ответов 58
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте. Есть задача: при помощи System.IO.Compression.GZipStream сжимать файлы. Программа должна эффективно распараллеливать и синхронизировать задачи в многопроцессорной среде.
основной вопрос: как правильно распараллелить все это?

изначально я думал так:
1 поток - считывает файл поблочно в некий пул блоков
2 поток - записывает сжатые блоки в файл
оставшиеся берут блоки из первого пула, сжимают их, и кладут в пул с которым работает второй поток.

но при детальном рассмотрении было обнаружено, что GZipStream сжимает байты в stream. Т.е. по сути я могу сжимать сразу в fileStream выходного файла. однако, если несколько потоков буду одновременно сжимать, не факт, что блоки будут записаны по порядку. Если же сжимать в memoryStream, а потом писать через CopyTo в fileStream, то при разжатии получаю пустой файл. если сжимаю в memoryStream, который потом преобразую в byte[], который пишу в fileStream, при разжатии получаю ошибку "Неправильное магическое число в заголовке GZip"

после всего этого не могу придумать ничего, кроме как один поток читает файл, другой сжимает. Но это не походит на "эффективное распараллеливание задач в многопроцессорной среде". Подскажите, пожалуйста, как быть
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
27.07.2017, 17:01
Ответы с готовыми решениями:

Многопоточное чтение - сжатие - запись файла
Здравствуйте. Необходимо выполнить компрессию файла, причем чтение, запись и сжатие должны...

Многопоточное чтение файла
Здравствуйте. Есть задача: читать большой файл блоками в x байт и потом с этими блоками что-то...

Многопоточное скачивание файла из интернета
Здравствуйте, хотел узнать следующее: можно ли как-нибудь с помощью многопоточности ускорить...

Многопоточное чтение строк из файла
нужно реализовать чтение строк из файла и передавать строку в функцию. Но это должно быть в...

58
3084 / 2226 / 641
Регистрация: 02.08.2011
Сообщений: 6,112
27.07.2017, 17:25 2
Цитата Сообщение от movorpovor Посмотреть сообщение
Если же сжимать в memoryStream, а потом писать через CopyTo в fileStream, то при разжатии получаю пустой файл
Вам нужно распараллелить именно сжатие байтов. Я сомневаюсь, что можно распараллелить операции с диском.
Схема примерно такая:
1. Прочитать файл
2. Разбить на блоки равной длины.
3. Параллельно сжать эти блоки, сохранив порядок.
4. Собрать блоки и записать в файл.
0
1452 / 845 / 150
Регистрация: 06.06.2012
Сообщений: 2,370
27.07.2017, 17:37 3
IamRain, movorpovor,

0. Добавить оперативы в комп))))
0
1 / 1 / 0
Регистрация: 24.05.2015
Сообщений: 45
27.07.2017, 17:43  [ТС] 4
IamRain а что подразумевается под "собрать блоки"
0
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
27.07.2017, 17:44 5
Цитата Сообщение от IamRain Посмотреть сообщение
4. Собрать блоки и записать в файл.
А как потом узнать границы блоков в файле?
0
1 / 1 / 0
Регистрация: 24.05.2015
Сообщений: 45
27.07.2017, 17:53  [ТС] 6
уточню вопрос кодом: как мне завести это? при разжатии созданного файла создается пустой файл
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using (var file = File.OpenRead("123.txt"))
{
    using (var fileToWrite = File.OpenWrite("1.gz"))
    {
        int part;
        while ((part = file.Read(buffer, 0, buffer.Length)) != 0)
        {
            var memory = new MemoryStream();
            var compressing = new GZipStream(memory, CompressionMode.Compress);
            compressing.Write(buffer, 0, part);
            var a = memory.ToArray();
            fileToWrite.Write(a, 0, a.Length);
        }
    }
}
0
3084 / 2226 / 641
Регистрация: 02.08.2011
Сообщений: 6,112
27.07.2017, 17:55 7
Цитата Сообщение от movorpovor Посмотреть сообщение
IamRain а что подразумевается под "собрать блоки"
Расположить байты блоков в том же порядке, в каком они были в исходном файле.

Цитата Сообщение от Fleder Посмотреть сообщение
А как потом узнать границы блоков в файле?
Блок - это просто кусок данных. Минуту, сейчас покажу.
0
1 / 1 / 0
Регистрация: 24.05.2015
Сообщений: 45
27.07.2017, 18:01  [ТС] 8
вот тут GZipstream - каков принцип многопоточного сжатия/распаковки? чувак делает по сути то же, что и я кодом выше, но чет не могу врубить почему у него работает, а у меня нет
0
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
27.07.2017, 18:01 9
Цитата Сообщение от IamRain Посмотреть сообщение
Блок - это просто кусок данных.
Если бы блоки были одинакового размера - то можно просто рассчитывать смещение.
Но разные куски исходного файла пожмутся в куски разного размера.
Смержим мы их в файле. А распаковывать как будем? Где границы?
0
3084 / 2226 / 641
Регистрация: 02.08.2011
Сообщений: 6,112
27.07.2017, 18:21 10
Цитата Сообщение от movorpovor Посмотреть сообщение
, но чет не могу врубить почему у него работает, а у меня нет
Посмотрите внимательно на свой код:
Цитата Сообщение от movorpovor Посмотреть сообщение
C#
1
2
var memory = new MemoryStream();
var compressing = new GZipStream(memory, CompressionMode.Compress);
Вы создаете пустой MemoryStream, а затем на его основе создаете GZipStream, который соответственно, тоже будет пустой, и затем записываете данные этого сжатого пустого массива в буфер. А далее вообще из пустого MemoryStream берете байты (которых нет) и записываете в выходной файл. У вас GZip как бэ вообще не работает.

Не тестировал, получилось вот так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
static void Main(string[] args)
        {
 
            //1
            var data = File.ReadAllBytes("path");
 
            //2
            var dataDivider = 10;
            var chunksCount = Math.DivRem(data.Length, dataDivider, out int remainder);
            if (remainder != 0)
                chunksCount++;
 
            var dataSize = data.Length / dataDivider;
 
            var listToHandle = new List<DataToHandle>(chunksCount);
 
            var counter = 1;
            for (int i = 0; i < chunksCount; i++)
               listToHandle.Add(new DataToHandle {OrderNumber = counter++,Data = data.Skip(i * dataSize).Take(dataSize).ToArray()});
            
 
            //3
            Parallel.ForEach(listToHandle, CompressData);
 
            //4
            File.WriteAllBytes("targetPath", AssembleData(listToHandle).ToArray());
 
            Console.ReadKey(true);
        }
 
 
        public static void CompressData(DataToHandle data)
        {
            using (var stream = new GZipStream(new MemoryStream(data.Data), CompressionMode.Compress, false))
            using (var targetStream = new MemoryStream())
            {
                stream.CopyTo(targetStream);
                data.Data = targetStream.ToArray();
            }
 
        }
 
        public static List<byte> AssembleData(List<DataToHandle> data)
        {
            var list = new List<byte>();
 
            foreach (var dataToHandle in data.OrderBy(d => d.OrderNumber))
                list.AddRange(dataToHandle.Data);
            return list;
        }
    }
 
    public class DataToHandle
    {
        public int OrderNumber { get; set; }
        public byte[] Data { get; set; }
    }
Добавлено через 3 минуты
По вышеприведенной ссылке отработает однозначно быстрее, так как читает поблочно и сразу же сжимает, в моем примере сразу все читает, так что будет очень накладно в случае файлов большого размера. Но это просто как пример распараллеливания.

Добавлено через 4 минуты
Цитата Сообщение от Fleder Посмотреть сообщение
А как потом узнать границы блоков в файле?
Их можно смотреть на основе DataToHandle.Data.Length объектов. Но ничего не тестировалось, это первое что пришло в голову. Так что, думаю, ТС-у проще разобрать пример из им же указанной ссылки.
0
1 / 1 / 0
Регистрация: 24.05.2015
Сообщений: 45
27.07.2017, 18:27  [ТС] 11
Цитата Сообщение от IamRain Посмотреть сообщение
Посмотрите внимательно на свой код:

Вы создаете пустой MemoryStream, а затем на его основе создаете GZipStream, который соответственно, тоже будет пустой, и затем записываете данные этого сжатого пустого массива в буфер. А далее вообще из пустого MemoryStream берете байты (которых нет) и записываете в выходной файл. У вас GZip как бэ вообще не работает.
если, честно, я все равно не врубаю.

вот построчный разбор моего кода, как я его вижу на языке смертных. поправьте, пожалуйста, где путаю:
1) var memory = new MemoryStream(); - создаю пустой memoryStream
2) var compressing = new GZipStream(memory, CompressionMode.Compress); - создаю GZipStream и указываю, что сжатые данные нужно транслировать в ранее созданный memoryStream
3) compressing.Write(buffer, 0, part); - прошу сжать buffer (получается, что GZipStream сжимает и пишет в memoryStream)
4) var a = memory.ToArray(); - получаю байты из memoryStream
5) fileToWrite.Write(a, 0, a.Length); - записываю байты в fileStream

где я не догоняю?
0
3084 / 2226 / 641
Регистрация: 02.08.2011
Сообщений: 6,112
27.07.2017, 18:31 12
Строки с 8 по 13 поправить на:
C#
1
2
3
4
5
//2
            var chunksCount = 10;
            var dataSize = Math.DivRem(data.Length, chunksCount, out int remainder);
            if (remainder != 0)
                chunksCount++;
0
3084 / 2226 / 641
Регистрация: 02.08.2011
Сообщений: 6,112
27.07.2017, 18:35 13
Смотрите внимательно: (пункт 2).
Миниатюры
Многопоточное сжатие файла  
0
3084 / 2226 / 641
Регистрация: 02.08.2011
Сообщений: 6,112
27.07.2017, 18:36 14
А исходный поток у вас пустой.

Добавлено через 1 минуту
Сравните с:
C#
1
2
3
4
5
6
7
8
9
10
 public static void CompressData(DataToHandle data)
        {
            using (var stream = new GZipStream(new MemoryStream(data.Data), CompressionMode.Compress, false))
            using (var targetStream = new MemoryStream())
            {
                stream.CopyTo(targetStream);
                data.Data = targetStream.ToArray();
            }
 
        }
0
1 / 1 / 0
Регистрация: 24.05.2015
Сообщений: 45
27.07.2017, 18:45  [ТС] 15
Цитата Сообщение от IamRain Посмотреть сообщение
А исходный поток у вас пустой.

Добавлено через 1 минуту
Сравните с:
C#
1
2
3
4
5
6
7
8
9
10
 public static void CompressData(DataToHandle data)
        {
            using (var stream = new GZipStream(new MemoryStream(data.Data), CompressionMode.Compress, false))
            using (var targetStream = new MemoryStream())
            {
                stream.CopyTo(targetStream);
                data.Data = targetStream.ToArray();
            }
 
        }
подскажите пожалуйста, что можно почитать по потокам (stream) потому как я не догоняю, зачем мне писать что-то в заполненный поток, и почему не в пустой

Добавлено через 5 минут
в выше указанном мною коде человек пишет
C#
1
2
3
4
5
6
7
8
            using (MemoryStream output = new MemoryStream(dataArray[(int)i].Length))
            {
                using (GZipStream cs = new GZipStream(output, CompressionMode.Compress))
                {
                    cs.Write(dataArray[(int)i], 0, dataArray[(int)i].Length);
                }
                compressedDataArray[(int)i] = output.ToArray();
            }
он тоже создает пустой memoryStream, но указывает его длину, и у него все работает.

скопировал ваш код, и выскочила ошибка "System.NotSupportedException: 'Поток не поддерживает чтение.'" на строчке stream.CopyTo(targetStream)
0
3084 / 2226 / 641
Регистрация: 02.08.2011
Сообщений: 6,112
27.07.2017, 18:46 16
Внимательного чтения документации из MSDN вполне хватит (Stream class). Хотя бы подсказки Intellisense внимательно читайте.
Комментарии на примере вашего кода:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using (var file = File.OpenRead("123.txt"))
{
    using (var fileToWrite = File.OpenWrite("1.gz"))
    {
        int part;
        while ((part = file.Read(buffer, 0, buffer.Length)) != 0)
        {
            // создать пустой поток данных (не содержащий ни единого байта)
            var memory = new MemoryStream();
            
            // на его основе создать еще один поток, сжав данные
            // поскольку данных не было, то этот поток тоже пустой
            var compressing = new GZipStream(memory, CompressionMode.Compress);
            // записать содержимое пустого потока в буфер
            compressing.Write(buffer, 0, part);
            // на основе пустого потока создать пустой массив
            var a = memory.ToArray();
            // записать пустой массив в файл
            fileToWrite.Write(a, 0, a.Length);
        }
    }
0
1 / 1 / 0
Регистрация: 24.05.2015
Сообщений: 45
27.07.2017, 19:09  [ТС] 17
IamRain вот вам ссылка на мсдн, почитайте внимательно что мы передаём первым параметром https://msdn.microsoft.com/en-... .110).aspx
0
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
27.07.2017, 19:10 18
IamRain, а вы уверенны, что строчка
C#
1
compressing.Write(buffer, 0, part);
записывает содержимое пустого потока в буфер?
А, может, наоборот, а?!
0
3084 / 2226 / 641
Регистрация: 02.08.2011
Сообщений: 6,112
27.07.2017, 19:15 19
Цитата Сообщение от movorpovor Посмотреть сообщение
cs.Write(dataArray[(int)i], 0, dataArray[(int)i].Length);
Здесь уже я ошибся, виноват. Stream.Write - записать в поток из буфера. В таком случае, проверяйте в отладке, есть ли данные в буфере.

Цитата Сообщение от movorpovor Посмотреть сообщение
скопировал ваш код, и выскочила ошибка "System.NotSupportedException: 'Поток не поддерживает чтение.'" на строчке stream.CopyTo(targetStream)
Ну я же сказал, я не тестировал.
0
1 / 1 / 0
Регистрация: 24.05.2015
Сообщений: 45
27.07.2017, 19:19  [ТС] 20
В любом случае спасибо вам. Если б не вы, я б не полез на мсдн и не увидел бы ремарки, что данные заносятся не сразу, и нужен Flush()
1
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
27.07.2017, 19:19
Помогаю со студенческими работами здесь

Сжатие файла с текстом
Здраствуйте. Нужна Ваша помощь. :) Как с помощью: using System.IO.Compression; Сжать файл,...

Сжатие текстового файла
Доброго времени суток! Мне вот дали задачу, типо из файла 750 КБ, сжать в 10 раз, тоесть вот...

Сжатие AVI файла
Здравствуйте. Столкнулся с проблемкой. Я пробую записать Image в AVI файл. Использую...

Очередь на многопоточное чтение файла
Привет, в общем из названия ясно что мне нужно, изначально сделал так: Private Function...

Сжатие файла
Добрый день! Каким образом сжимают скрипты? Например, по типу jquery.min.js. Там весь код идёт в...

сжатие бинарного файла
Подскажите метод,который наиболее подходил бы по сжатию бинарного файла...заранее спасибо)))


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2023, CyberForum.ru