public ConsumerThread(ConsumerThreadParams param) { _params = param; _hashAlgorithm = HashAlgorithm.Create(param.HashAlgorithmName); _currentThread = new Thread(new ThreadStart(DoWork)); _currentThread.Name = param.ThreadName; Debug.WriteLine($"Consumer thread {_currentThread.ManagedThreadId} is created"); ThreadCounter.Increment(); _currentThread.Start(); }
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 void DoWork() { try { while (!_params.ExitByErrorEvent.IsSet) { Debug.WriteLine($"Consumer thread {_currentThread.ManagedThreadId} is trying to get memory block"); // Получаем блок файла для хэширования if (!_params.ReadyToGetMemoryBlocks.TryDequeue(out var currentChunk)) { // Если мы не получили блок и получили сообщение об окончании файла - гасим поток if (_params.ExitByFileEndingEvent.IsSet) { Debug.WriteLine($"Consumer thread {_currentThread.ManagedThreadId} is shutting down"); break; } // Если мы не получили блок и сообщения об окончании файла еще не было, то другие потоки опередили текущий else { Debug.WriteLine($"Consumer thread {_currentThread.ManagedThreadId} got nothing"); continue; } } Debug.WriteLine($"Consumer thread {_currentThread.ManagedThreadId} got chunk #{currentChunk.ChunkIndex}"); var hashBytes = _hashAlgorithm.ComputeHash(currentChunk.MemoryBlock); // Блок файла прохеширован, область памяти можно освобождать под следующий блок _params.ReleasedMemoryBlocks.Enqueue(currentChunk.MemoryBlock); _params.ResultWriter.Write(currentChunk.ChunkIndex, hashBytes); } } catch (Exception e) { Debug.WriteLine($"Consumer thread {_currentThread.ManagedThreadId} caused exception"); Debug.WriteLine(e); _params.NotifyProducerAboutError(e); _params.ExitByErrorEvent.Set(); } ThreadCounter.Decrement(); _hashAlgorithm.Dispose(); }