Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ответы Word.docx
Скачиваний:
25
Добавлен:
16.03.2016
Размер:
352.09 Кб
Скачать

Критические секции

Работают они следующим образом: внутри критической секции может работать только один поток, другие ждут его завершения. Чтобы лучше понять, везде приводят сравнение с узкой трубой: представьте, с одной стороны "толпятся" потоки, но в трубу может "пролезть" только один, а когда он "пролезет" - начнёт движение второй, и так по порядку. Еще проще понять это на примере и тем же ProgressBar'ом. Итак, запустите один из примеров, приведённых ранее. Нажмите на кнопку, подождите несколько секунд, а затем нажмите еще раз. Что происходит? ProgressBar начал прыгать. Прыгает потому, что у нас работает не один поток, а два, и каждый из них передаёт разные значения прогресса. Теперь немного переделаем код, в событии onCreate формы создадим критическую секцию:

var   Form1: TForm1;   CriticalSection: TCriticalSection; ... procedure TForm1.FormCreate(Sender: TObject); begin   CriticalSection:=TCriticalSection.Create; end;

У TCriticalSection есть два нужных нам метода, Enter и Leave, соответственно вход и выход из неё. Поместим наш код в критическую секцию:

procedure TNewThread.Execute; var   i: integer; begin   CriticalSection.Enter;   for i:=0 to 100 do   begin     sleep(50);     SendMessage(Form1.Handle,PROGRESS_POS,0,i);   end;   CriticalSection.Leave; end;

Попробуйте запустить приложение и нажать несколько раз на кнопку, а потом посчитайте, сколько раз пройдёт прогресс. Понятно, в чем суть? Первый раз, нажимая на кнопку, мы создаём поток, он занимает критическую секцию и начинает работу. Нажимаем второй - создаётся второй поток, но критическая секция занята, и он ждёт, пока её не освободит первый. Третий, четвёртый - все пройдут только по очереди.

Критические секции удобно использовать при обработке одних и тех же данных (списков, массивов) разными потоками. Поняв, как они работают, Вы всегда найдёте им применение.

Пример создания многопоточного приложения в Delphi:

Этот раздел содержит описание шагов, необходимых для создания простого, но показательного примера многопоточного приложения. Мы будем пытаться вычислить число "пи" с максимальной точностью после запятой. Конечно, встроенная в Delphi константа Piимеет достаточную точность, правильнее сказать — максимальную, допускаемую самым точным 10-байтным форматом для вещественных чиселExtended. Так что превзойти ее нам не удастся. Но этот пример использования потоков может послужить прологом для решения реальных задач.

Первый пример будет содержать два потока: главный (обрабатывающий ввод пользователя) и вычислительный; мы сможем изменять их свойства и наблюдать за реакцией. Итак, выполните следующую последовательность действий:

1. В среде Delphiоткройте менюFileи выберите пунктNew Application.

2. Расположите на форме пять меток и один переключатель, как показано на рис. 1. Переименуйте главную форму в fmMain.

3. Откройте меню Fileи выберите пунктSave Project As. Сохраните модуль какuMain, а проект — какThreads 1.

Рис. 1.Внешний вид формы для приложения Threads'1

4. Откройте меню Fileи выберите пунктNew. Затем дважды щелкните на объекте типа поток (значокThread Object). Откроется диалоговое окноNew Items, показанное на рис. 2.

Рис. 2.Диалоговое окноNew Itemsс выбранным объектом типа "поток"

Рис. 3. Диалоговое окно New Thread Object

5. Когда появится диалоговое окно для именования объекта поток, введите TPiThreadи нажмите клавишуEnter(рис. 3). Помимо этого, при желании, вы можете присвоить создаваемому потоку имя, установив флажокNamed Threadи задав имя в полеThread Name. Так как имя потока используется только для удобства обозначения, эту возможность мы использовать не будем.

Delphi создаст новый модуль и поместит в него шаблон для нового потока.

6. Код, вносимый в метод Execute, вычисляет числоPi, используя сходимость бесконечного ряда Лейбница:

Pi = 4 - 4/3 + 4/5 - 4/7 + 4/9 -...

Разумеется, отображать новое значение после каждой итерации — это то же самое, что стрелять из пушки по воробьям. На отображение информации система потратит в десятки раз больше времени, чем на собственно вычисления. Поэтому мы ввели константу updatePeriod, которая регулирует периодичность отображения текущего значения.

Код метода Execute показан ниже:

7. Откройте меню Fileи выберите пунктSave As. Сохраните модуль с потоком какuPiThread.pas.

8. Отредактируйте главный файл модуля uMain.pasи добавьте модульuPiThreadк списку используемых модулей в секции интерфейса. Он должен выглядеть так:

9. В секции publicформыTfmMainдобавьте ссылку на создаваемую нить:PiThread : TPiThread;

10. Добавьте в модуль uMainдве глобальные переменные

и метод UpdatePi:

Этот метод, если Вы обратили внимание, вызывается из потока посредством процедуры Synchronize. Он отображает текущее значение приближения к числу "пи" и количество итераций.

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

11. Выполните двойной щелчок на свободном месте рабочей области формы, при этом создастся шаблон метода FormCreate. Здесь мы отобразим значение системной константы р±:

12. Выберите на форме переключатель (его название cbcalculate) и назначьте событиюOnclickкод, создающий и уничтожающий вычислительный поток в зависимости от состояния переключателя:

Таким образом, многопоточное приложение готово к запуску. Если все пройдет нормально, вы увидите картинку, подобную той, которая приведена на рис. 4.

Рис. 4.Выполняющееся приложениеThreads1 Вопросы для подготовки к сдаче лабораторной работы:

  1. Что такое поток?

  2. Какие Вы знаете типы многозадачности в ОС Windows?

  3. Что такое процесс?

  4. В чем отличие потока от процесса?

  5. Для чего необходима процедура Synchronize?

  6. Для чего необходимы критические секции?

  7. Какие Вы знаете приоритеты потоков?

  8. Для чего необходим метод Execute?

  9. Как можно определить количество потоков в запущенном процессе?