예제 #1
0
        public void SplitFileAndCalculateHashes(Stream fileStream, int blockSize, string hashAlgorithmName, IBufferedResultWriter resultWriter)
        {
            // Это позволяет нам не создавать слишком большой массив буффера, если файл сам по себе меньше размера блока
            blockSize = CalculateBlockSize(fileStream.Length, blockSize);

            // Расчитываем, сколько мы можем создать блоков в памяти
            var amountOfBlocks = CalculateAmountOfBlocks(blockSize);

            // Счетчик потоков поможет нам правильно завершить работу
            var allThreadsAreCompletedEvent = new AutoResetEvent(false);

            ThreadCounter.Initialize(allThreadsAreCompletedEvent);

            // Эти события будут доставлять информацию в другие потоки
            var closeThreadsByFileEndingEvent = new ManualResetEventSlim(false);
            var closeThreadsByErrorEvent      = new ManualResetEventSlim(false);

            // Алгоритм работает на основе двух очередей: readyToGetMemoryBlocks (доступные для хэширования) и releasedMemoryBlocks (доступные для перезаписи)
            // Прочитанный блок файла записывается в уже созданный буфер, затем отправляется в очередь readyToGetMemoryBlocks.
            // Оттуда его подхватывает первый попавшийся consumer-поток, делает свои дела и затем ссылку на буфер отправляет в releasedMemoryBlocks, таким образом сообщая, что буфер доступен для следующего блока
            // Из этой очереди producer-поток достает свободный буфер, записывает в него блок файла и опять отправляет в очередь readyToGetMemoryBlocks
            var memoryBlockIsReleasedEvent = new ManualResetEventSlim(true);
            var releasedMemoryBlocks       = new MemoryBlocksManager <byte[]>(amountOfBlocks, memoryBlockIsReleasedEvent, closeThreadsByFileEndingEvent, closeThreadsByErrorEvent);

            var memoryBlockIsReadyToGetEvent = new ManualResetEventSlim(true);
            var readyToGetMemoryBlocks       = new MemoryBlocksManager <ReadyToGetMemoryBlock>(amountOfBlocks, memoryBlockIsReadyToGetEvent, closeThreadsByFileEndingEvent, closeThreadsByErrorEvent);

            var chunkIndex    = 1;
            var numberOfBytes = 1;

            byte[] currentBuffer;

            try
            {
                // Первичное чтение файла, ограниченное количеством блоков amountOfBlocks
                // Здесь мы создаем буферы, создаем потоки и уже начинаем хэшировать файл
                for (; chunkIndex <= amountOfBlocks && !closeThreadsByErrorEvent.IsSet; chunkIndex++)
                {
                    currentBuffer = new byte[blockSize];

                    numberOfBytes = fileStream.Read(currentBuffer, 0, blockSize);
                    if (numberOfBytes == 0)
                    {
                        SuccessfullExit();
                        return;
                    }

                    if (blockSize > numberOfBytes)
                    {
                        currentBuffer = currentBuffer[0..numberOfBytes];
 public ConsumerThreadParams(
     string threadName,
     MemoryBlocksManager <byte[]> releasedMemoryBlocks,
     MemoryBlocksManager <ReadyToGetMemoryBlock> readyToGetMemoryBlocks,
     string hashAlgorithmName,
     IBufferedResultWriter resultWriter,
     ManualResetEventSlim exitByFileEndingEvent,
     ManualResetEventSlim exitByErrorEvent,
     Action <Exception> errorNotifierMethod)
 {
     ThreadName               = threadName;
     ReleasedMemoryBlocks     = releasedMemoryBlocks;
     ReadyToGetMemoryBlocks   = readyToGetMemoryBlocks;
     HashAlgorithmName        = hashAlgorithmName;
     ResultWriter             = resultWriter;
     ExitByFileEndingEvent    = exitByFileEndingEvent;
     ExitByErrorEvent         = exitByErrorEvent;
     NotifyProducerAboutError = errorNotifierMethod;
 }