/// <summary>
        /// Считывает часть данных из файла и помещает ее в очередь.
        /// </summary>
        /// <param name="obj">Поток, который работает с данным методом (класса ManualThread)</param>
        /// <exception cref="ArgumentException">Если аргумент метода не является объектом класса ManualThread</exception>
        private void ReadDataPart(object obj)
        {
            if (!(obj is ManualThread))
            {
                throw new ArgumentException("Метод чтения данных требует объект класса ManualThread");
            }

            ManualThread worker = obj as ManualThread;
            byte[] buffer = new byte[PartSize];
            long fileLength = inputStream.BaseStream.Length;
            ulong totalRam = Options.GetTotalRAM();

            while (fileLength > 0)
            {
                ulong ram = Options.GetAvailableRAMInBytes();

                if (ram < (uint)(PartSize * NumThreads) || ram < (totalRam / 100.0) * swapBoundary)
                {
                    Thread.Sleep(10);
                    if (queue.Count == 0)
                    {
                        GC.Collect();
                    }
                    continue;
                }

                int numBytes = inputStream.Read(buffer, 0, PartSize);
                if (numBytes == 0)
                {
                    break;
                }

                byte[] realPart = null;

                try
                {
                    realPart = new byte[numBytes];
                }
                catch (OutOfMemoryException)
                {
                    GC.Collect();
                    if (IsWorkInProgress())
                    {
                        while(queue.Count > 0)
                        {
                            Thread.Sleep(100);
                        }
                    }
                    realPart = new byte[numBytes];

                }

                Array.Copy(buffer, realPart, numBytes);
                queue.Enqueue(new NumberedPart(partIndex, realPart));
                partIndex++;
            }

            worker.Complete();
        }
        /// <summary>
        /// Выполняет извлечение части данных из очереди, шифрует их с помощью SHA256 и выводит в стандартный поток вывода
        /// </summary>
        /// <param name="obj">Поток, который работает с даннным методом (класса ManualThread)</param>
        /// <exception cref="ArgumentException">Если аргумент метода не является объектом класса ManualThread</exception>
        /// <exception cref="InvalidOperationException">Если в очереди был объект класса типа, отличного от NumberedPart</exception>
        private void ConvertToSHA(object obj)
        {
            if (!(obj is ManualThread))
            {
                throw new ArgumentException("Метод шифрования требует объект класса ManualThread");
            }

            ManualThread worker = obj as ManualThread;

            while (true)
            {
                if (queue.Count == 0)
                {
                    if (!reader.IsWorkCompleted)
                    {
                        continue;
                    }
                    else
                    {
                        worker.Complete();
                        break;
                    }
                }
                else
                {
                    NumberedPart part = null;

                    lock (dequeueSync)
                    {
                        if (queue.Count > 0)
                        {
                            var tempPart = queue.Dequeue();
                            if (tempPart is NumberedPart)
                            {
                                part = tempPart as NumberedPart;
                            }
                            else
                            {
                                throw new InvalidOperationException("В очередь попал объект класса, отличающийся от NumberedPart");
                            }
                        }
                    }

                    if (part != null)
                    {
                        Console.WriteLine($"Часть № {part.Number}\n{Sha256(part.Data)}\n");
                    }
                }
            }
        }
        public SHAConverter(Options options)
        {
            PartSize = options.PartSize;
            FileName = options.Filename;
            inputStream = new BinaryReader(new FileStream(options.Filename, FileMode.Open, FileAccess.Read, FileShare.Read));
            NumThreads = options.NumThreads;

            reader = new ManualThread(new Thread(ReadDataPart));

            for (int i = 0; i < NumThreads; i++)
            {
                ManualThread worker = new ManualThread(new Thread(ConvertToSHA));
                workers.Add(worker);
            }
        }