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; }