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; }
/// <inheritdoc/> public void SplitFileAndCalculateHashes(Stream fileStream, int blockSize, string hashAlgorithmName, IBufferedResultWriter resultWriter) { var bytesLeft = fileStream.Length; byte[] buffer = new byte[blockSize]; var chunkIndex = 1; var numberOfBytes = 0; using var hashAlgorithm = HashAlgorithm.Create(hashAlgorithmName); while (true) { // Это позволяет нам не создавать слишком большой массив буффера, если файл сам по себе меньше размера блока // А так же, если мы находимся в последнем блоке, часть массива будет занята нулями, // чтобы не расчитывать хэш для нулевой части, укажем явно границы массива if (bytesLeft < blockSize) { blockSize = (int)bytesLeft; } numberOfBytes = fileStream.Read(buffer, 0, blockSize); if (numberOfBytes == 0) { break; } var hashBytes = hashAlgorithm.ComputeHash(buffer, 0, blockSize); resultWriter.Write(chunkIndex, hashBytes); bytesLeft = fileStream.Length - fileStream.Position; chunkIndex++; } }
/// <inheritdoc/> public void SplitFileAndCalculateHashes(string path, int blockSize, string hashAlgorithmName, IBufferedResultWriter resultWriter) { using var fileStream = File.OpenRead(path); SplitFileAndCalculateHashes(fileStream, blockSize, hashAlgorithmName, resultWriter); }
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];