Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Учебное пособие 800564

.pdf
Скачиваний:
3
Добавлен:
01.05.2022
Размер:
5.57 Mб
Скачать

Продолжение табл. 3

5.Необработанные аварийные сигналы

6.Сигналы и их обработчики

7.Информация об использовании ресурсов

Задание 1. Разработать программу, включающую процедуры расчета решета Эратосфена, чисел Фибоначчи, факториала.

Сделать приложение многопоточным, следуя инструкции в описании, которое приведено ниже. Показать два приложения.

Организовать вывод промежуточных значений расчета всех процедур в одно окно выдачи, подписав конкретный алгоритм.

Выполнение задания 1:

1) Основы работы объекта TThread

Класс TThread является прямым потомком класса TObject и, следовательно, не является компонентом. Нетрудно заметить, что метод TThread.Execute абстрактный. Это значит, что класс TThread сам является абстрактным, и вы никогда не сможете создать экземпляр самого класса TThread. Вам можно создавать только экземпляры потомков класса TThread.

Для создания функционального потомка класса TThread надо переопределить единственный метод - Execute.

Единственный логический параметр, который передается в конструктор Create класса TThread, называется CreateSus-

41

pended и показывает, начинать ли поток в приостановленном состоянии. Если этот параметр равен False, метод Execute этого объекта будет автоматически вызван без промедления. Если этот параметр равен True, то для действительного начала работы потока в определенной точке приложения нужно воспользоваться методом Resume, который приведет к вызову метода Execute. Обычно параметр CreateSuspended устанавливаемся равным True в случае, когда перед работой потока нужно установить дополнительные свойства для потокового объекта. Установка же свойств после запуска потока может привести к возникновению проблем.

Если же метод в рамках потока будет иметь тип результата void (т.е. отсутствие типа) с пустым списком параметров,

тогда создается делегат типа System.Threading.ThreadStart.

Таким образом, основной фрагмент листинга 1 (однопоточная версия) с последовательным вызовом трех функций:

Number (nNum);

Fibonacci (nFib);

Factorial (nFac);

можно трансформировать в соответствующий фрагмент листинга 2 (трехпоточная версия):

Thread thread1 = new Thread(new ParameterizedThreadStart(Number)); thread1.Start(nNum);

Thread thread2 = new Thread(new ParameterizedThreadStart(Fibonacci)); thread2.Start(nFib);

Thread thread3 = new Thread(new ParameterizedThreadStart(Factorial)); thread3.Start(nFac);

thread1.Join();

thread2.Join();

thread3.Join();

42

Здесь метод Start запускает отдельную нить вычислений и передает в нее соответствующую переменную, а метод Join ожидает реального завершения указанного потока.

Кстати, если бы ставилась гипотетическая задача передать результат вычисления функции Number в функцию Fibonacci в качестве аргумента, то перед запуском нити thread2 возникла бы необходимость дождаться завершения выполнения потока thread1 методом thread1.Join(), а уж затем запускать thread2.

Следует заметить, что метод Thread.Join является перегруженным, и факультативная переменная типа Int32 задавала бы время ожидания в миллисекундах. Так, метод thread3.Join(5000) ждал бы завершения потока в течение 5 с, после чего можно было бы форсировать завершение вычислительной нити thread3 методом thread3.Abort(), не дожидаясь вывода результатов.

В языке C# отдельный поток может находиться в одном из следующих состояний (определяемом свойством

Thread.ThreadState):

-незапущенный (Unstarted),

-исполнение (Running),

-ожидание (WaitSleepJoin),

-приостановленный (Suspended),

-прерванный (Aborted),

-завершенный (Stopped)

Каждое состояние характеризуется своими методами, и детальную информацию о них (и не только) следует искать в удобной системе помощи Microsoft Visual Studio.

Многопоточность становится востребованной, если это не просто модная конструкция программного кода, а хорошо продуманный алгоритм, обеспечивающий эффективное исполнение задач как на одноядерных платформах, так и на многоядерных компьютерах.

43

2) Описание методов

Решето Эратосфена - метод, разработанный Эратосфеном и позволяющий отсеивать составные числа из натурального ряда. Сущность его заключается в следующем.

Зачеркивается единица. Число 2 - простое. Зачеркиваются все натуральные числа, делящиеся на 2. Число 3 - первое незачеркнутое число - будет простым. Далее зачеркиваем все натуральные числа, которые делятся на 3. Число 5 - следующее незачеркнутое число - будет простым. И так можно продолжать до бесконечности.

Числа Фибоначчи - это элементы числовой последовательности 1, 1, 2, 3, 5, 8, 13... , в которой каждый последующий член равен сумме двух предыдущих, а сама эта последовательность называется рядом Фибоначчи.

Таким образом, число Фибоначчи можно вычислить по формуле Fibonacci(n) = Fibonacci(n-2) + Fibonacci(n-1).

Например, 7-е число из ряда Фибоначчи соответствует

Fibonacci(7) = Fibonacci(5) + Fibonacci(6) = 5 + 8 = 13.

Факториал - это произведение натуральных чисел от единицы до какого-либо данного натурального числа n (т. е. 1*2*3*...*n), обозначается "n!". Например, 7! = 1*2*3*4*5*6*7

=5040.

3)Создание многопоточного приложения

Эффективное управление потоками дело хлопотное и не

во всех случаях приводит к росту производительности.

В частности, нет большого смысла в попытках мультипрограммирования решета Эратосфена, поскольку в нем натуральный ряд отбирается постепенным отсеиванием составных чисел. Или, например, алгоритм определения некоторого числа

44

из ряда Фибоначчи не удастся сделать многопоточным, так как каждый последующий член ряда равен сумме двух предыдущих, а значит, надо шаг за шагом определять весь ряд. Да и ни к чему "параллелить" алгоритм определения факториала (n!=1*2*3*...*n), поскольку факториал числа 65 вычисляется за считанные миллисекунды, а подсчитать факториал большего числа уже проблематично, ведь разрядность целочисленных переменных в компиляторах имеет свои ограничения (например, переменная типа ulong является беззнаковым целым от 0

до 18446744073709551615).

Необходимо решить три перечисленные задачи в одном алгоритме, поэтому возможны любопытные варианты.

Можно написать классический последовательный код. В этом решении из тела программы в строгой последовательности вызываются основные функции:

-Number (для нахождения максимального простого числа в определенном диапазоне от 1 до nNum),

-Fibonacci (для определения члена под номером nFib из ряда Фибоначчи)

-Factorial (для вычисления факториала числа nFac), которые проводят соответствующие вычисления для целочисленных аргументов nNum, nFib, nFac.

По умолчанию nNum = 200 000, nFib = 500 000 000, nFac

=65, но при желании в исполнительный процесс можно передать другие параметры.

Пример выполнения задания 1:

Результат работы программы представлен на рис. 33.

45

Рис. 33. Результат работы многопоточного приложения

Пример листинга программы представлен по фрагментам на рис. 34 – рис. 38.

46

Рис. 34. Фрагмент 1 листинга

47

Рис. 35. Фрагмент 2 листинга

48

Рис. 36. Фрагмент 3 листинга

49

Рис. 37. Фрагмент 4 листинга

50