Ejemplo n.º 1
0
        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();
        }
Ejemplo n.º 2
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];
Ejemplo n.º 3
0
        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();
        }