Привет, Гость! Регистрация RSS
Среда, 24.04.2024
Главная » Статьи » Уроки

Потоки (Threads). Создание многопоточных приложений

В операционной системе Windows предусмотрена возможность выполнения в приложении нескольких потоков. Поток (Tthread) – это объект ОС, который представляет собой отдельный путь выполнения программы внутри определенного процесса. Поток – это код, который входит в состав программы, пользуется ее ресурсами и выполняется в ее адресном пространстве.  С помощью потоков реализуются средства одновременного выполнения нескольких различных подпрограмм, каждой из которых ОС выделяет небольшой квант времени, в течение которого она выполняется, а затем происходит переключение к следующей. Так как квант времени очень мал, для человека такое мгновенное переключение незаметно. В результате создается иллюзия одновременной работы нескольких программ. Процессорное время ОС разделяет между разными приложениями и потоками на основе вытеснения. Разделение происходит в основном благодаря приоритету потока. У каждого потока есть приоритет, по которому определяется его важность. Чем выше приоритет, тем больше процессорного времени ему выделяется. Потоки с одинаковым приоритетом будут получать одинаковое количество процессорного времени.

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

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

Для наглядного представления создадим приложение, демонстрирующего работу потока.

Программа запускает поток нажатием кнопки Запустить и в компоненте TLabel будет работать счетчик. В это же время можно без проблем набирать текст в RichEdit

1. Создаем новый проект. Устанавливаем на форму компонент TRichEdit с вкладки Win32 и один компонент TLabel. Еще понадобится пара кнопок - одна для запуска потока, другая для его остановки.

2. Теперь создаем модуль для потока. Для этого выбираем пункт меню File | New | Other для открытия окна создания нового модуля.. Выделить Thread Object и нажмите кнопку ОК. В результате появится окно:

3. В этом окне нужно указать имя создаваемого потока – TCountobj. Нажать кнопку ОК, и Delphi создаст модуль-заготовку для будущего потока. Сохраните весь проект – главную форму под именем Main, поток под именем MyThread.

У объекта потока есть только одна процедура Execute (заготовка для программного кода потока), при его создании она объявлена как абстрактная (abstract) - пустая. Это значит, что процедуре дали имя, выделили место в памяти, но ее код в любых потоках должен быть переопределен, т.е. должен быть написан собственный код, который будет выполняться параллельно основной задаче.

procedure TCountObj.Execute;
begin
 index := 1;
 //Запускаем бесконечный счетчик
while index > 0 do
 begin
 Synchronize(UpdateLabel);
 Inc(index);
 if index > 100000 then
 index:=0;
 //Если поток остановлен, то выйти.
 if terminated then exit;
 end;
end;

Переменная index объявлена как integer в разделе private объекта потока. Там же объявлена процедура UpdateLabel. Эта процедура выглядит так:

procedure TCountObj.UpdateLabel;
begin
 Forml.Labell.Caption:=IntToStr(Index);
end;

4. Далее в модуле потока подключаем главную форму в раздел uses,потому что обращение к ней происходит в коде выше (Form1.Label1.Caption)для обновления текста компонента Label1. В методе Execute запускается цикл while, который будет выполняться, пока переменная index больше нуля. Внутри цикла вызывается метод synchronize() и увеличивается переменная index. Если эта переменная становится больше 100 000, то в index присваивается 0 и расчет прекращается. Таким образом, цикл будет бесконечно выполнять увеличение переменной index от 0 до 100000. Последней идет проверка следующего характера, если свойство terminated равно true, то выйти из процедуры и работа потока закончится. Свойство terminated станет равным true тогда, когда будет вызван метод Terminate потока.

5. О функции synchronize. В качестве параметра ей передается процедура updateLabel, которая производит вывод в главную форму. Для чего нужно вставлять процедуру вывода на экран в synchronize? Библиотека VCL имеет один недостаток – она не защищена от потоков. Все пользовательские компоненты разрабатывались так, что к ним может получить доступ только один поток. Если главная форма и поток попробуют одновременно вывести что-нибудь в одну и ту же область экрана или компонент, то программа рухнет. Поэтому весь вывод на форму нужно выделять в отдельную процедуру и вызывать эту процедуру с помощью Synchronize.

Если процедура вызвана в методе Synchronize, то к компонентам окна получает доступ только объект, вызвавший метод synchronize. Этот процесс незаметен для пользователя.

Таким образом, если нужно вывести какие-то данные из потока на экран главного окна, то делайте это в отдельной процедуре и вызывайте ее с помощью Метода Synchronize. Поток готов.

6.  В разделе uses главной форме (самый первый, который идет после interface) добавить модуль потока MyThread. Почему именно в этот раздел, а не тот, что расположен ниже? Это связано с тем, что в разделе private нужно объявить переменную, имеющую тип объекта потока. Если добавить имя модуля во второй раздел uses, то он должен находиться ниже той части кода, где нужно написать объявление.

      Именно поэтому добавлять модуль MyThread нужно в первый раздел uses.

      В разделе private объявить переменную со типа TCountobj (объект потока).

      По нажатии кнопки Запустить написать код:

procedure TForml.ButtonlClick(Sender: TObject);
begin
 со:=TCountObj.Create(true);
 со.Resume;
 со.Priority:=tpLower;
end;

В первой строке кода создается поток со. В качестве параметра может быть true или false. Если false, то поток сразу начинает выполнение, иначе поток создается, но не запускается. Если поток создан незапушенным, то для запуска нужно использовать метод Resume, что и происходит во второй строке. В третьей строке устанавливается приоритет потока поменьше, чтобы он не мешал работе основного потока и выполнялся в фоновом режиме. Если установить приоритет повыше, то основной поток начнет притормаживаться, потому что у них будут одинаковые приоритеты.

Для события onclick кнопки Остановить написать следующий код:

procedure TForml.ButtonlClick(Sender: TObject);
begin
 со.Terminate;
end;

Здесь происходит остановка выполнения потока с помощью вызова метода Terminate объекта потока. После вызова этого метода свойство terminated станет равным true и выполнение процедуры Execute заканчивается.

Довольно часто возникает ситуация, когда хочется приготовить беляши, а времени для хлопот с дрожжевым тестом катастрофически не хватает. В этом случае весьма кстати будет тесто бездрожжевое на kz.all.biz

7. Запускаем программу, запускаем поток (нажатием кнопки Запустить) и набираем текст в RichEdit. Текст будет набираться без проблем, и в это время в компоненте TLabel будет работать счетчик. Если бы вы запустили счетчик без отдельного потока, то не смогли бы набирать текст в RichEdit, потому что все ресурсы программы (основного потока) уходили бы на работу счетчика.

 

Нравится
Категория: Уроки | Добавил: Dark_Green (01.02.2015)
Просмотров: 9629 | Рейтинг: 4.0/6

Другие статьи
»
TreeView: отображение иерархических данных (0)
»
Создание и использование MDI (0)
»
Рисование при выполнении программы (0)
»
Создание и использование SDI (2)
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]