Использование многопоточности в дополнениях Delphi
Концепция потоков
Потек (thread) - это объект операционной системы, которая являет собой отдельный путь выполнения программы внутри определенного процесса. Каждое дополнение Win32 имеет, по крайней мере, один поток, обычно называемый первичным, или главным, но программы имеют право создавать дополнительные потоки, предназначенные для выполнения других заданий.
С помощью потоков реализуются средства одновременного выполнения нескольких разных подпрограмм. Конечно, если компьютер оснащен только одним процессором, то о настоящей одновременности работы двух потоков говорить не придется. Но когда для обработки каждого потока операционная система по очереди выделяет определенное время (измеряется в мельчайших долях секунды), создается впечатление одновременной работы нескольких дополнений.
Использование многопоточности в дополнениях Delphi
Следовательно, давайте определимся, что под словом "потек" мы имеем в виду именно Thread, который еще имеет название "нить". Нередко встречаются на форумах мысли, что потоки не нужны вообще, любую программу можно написать так, что она будет замечательно работать и без них. Конечно, если не делать ничего серьезнее "Hello World" это так и есть, но если постепенно набирать опыт, рано или поздно любой программист подойдет к возможности "плоского" кода, возникнет необходимость распараллеливать задание. А некоторые задачи вообще нельзя реализовать без использования потоков, например работа с сокетами, COM -портом, длительное ожидание каких-либо событий, и так далее
Всем известно, что Windows система многозадачна. Проще говоря, это значит, что несколько программ могут работать одновременно под управлением ОС. Все мы открывали диспетчер заданий и видели список процессов. Процесс - это экземпляр выполняемой программы. В действительности сам по себе он ничего не выполняет, он создается во время запуска программы, содержит в себе служебную информацию, через которую система с ним работает, так же ему выделяется необходимая память под код и данные. Для того, чтобы программа заработала, в нем создается поток. Любой процесс содержит в себе хотя бы один поток, и именно он отвечает за выполнение кода и получает на это процессорное время. Этим и достигается мнимая параллельность работы программ, или, как ее еще называют, псевдопараллельность. Почему мнимая? Но потому, что реально процессор в каждый момент времени может выполнять только один участок кода. Windows раздает процессорное время всех потоков в системе по очереди, тем же создается впечатление, что они работают одновременно. Реально работают параллельно потоки могут быть только на машинах с двумя и больше процессорами.
Для создания дополнительных потоков в Delphi существует базовый класс TThread, от него мы и будем наследоваться при реализации своих потоков. Для того, чтобы создать "скелет" нового класса, можно выбрать в меню File - New - Thread Object, Delphi создаст новый модуль с заготовкой этого класса. Для наглядности, опишем его в модуле формы. Как Вы видите, в этой заготівці прибавлено один метод - Execute. Именно его нам и надо переопределить, код внутри него и будет работать в отдельном потоке. И так, попробуем написать пример - запустим в потоке бесконечный цикл:
TNewThread = class (TThread)
private
(Private declarations)
protected
procedure Execute; override;
end;
var
Form1: TForm1;
implementation
($ R *. dfm)
(TNewThread)
procedure TNewThread.Execute;
begin
while true do (ничего не делаем);
end;
procedure TForm1.Button1Click (Sender: TObject);
var
NewThread: TNewThread;
begin
NewThread: = TNewThread.Create (true);
NewThread.FreeOnTerminate: = true;
NewThread.Priority: = tpLower;
NewThread.Resume;
end;
Запустите пример на выполнение и нажмите кнопку. Вроде бы ничего не происходит - форма не зависшая, реагирует на перемещение. В действительности это не так - откройте диспетчер заданий и вы увидите, что процессор загружен полностью. Сейчас в процессе вашего приложения работает два потока - один был создан сначала, во время запуска программы. Второй, который так грузит процессор, - мы создали после нажатия кнопки. Следовательно, давайте разберем, что же означает код в Button1Click:
NewThread: = TNewThread.Create (true);
здесь мы создали экземпляр класса TNewThread. Конструктор Create имеет всего один параметр - CreateSuspended типа boolean, который указывает, запустить новый поток сразу после создания (если false), или дождаться команды (если true).
New.FreeOnTerminate: = true;
свойство FreeOnTerminate определяет, что потек после выполнения автоматически завершится, объект будет уничтожен, и нам не придется его уничтожать вручную. В нашем примере это не суть важно, потому что сам по себе он никогда не завершится, но понадобится в следующих примерах.
NewThread.Priority: = tpLower;
Свойство Priority, если вы еще не догадались из названия, устанавливает приоритет потока. Каждый поток в системе имеет свой приоритет. Если процессорного времени не хватает, система начинает распределять его в соответствии с приоритетами потоков. Свойство Priority может принимать следующие значения:
• tpTimeCritical - критический
• tpHighest - очень высокий
• tpHigher - высокий
• tpNormal - средний
• tpLower - низкий
• tpLowest - очень низкий
• tpIdle - поток работает во время простоя системы
Ставить высокие приоритеты потоков не стоит, если этого не требует задание, потому что это сильно нагружает систему.
NewThread.Resume;
Запуск потока.
Это был пример создания потоков. Но не все так просто. Казалось бы - пишем любой код внутри метода Execute и все, но, потоки имеют одно неприятное свойство - они ничего не знают друг о друге. Это значит, допустимо, Вы пытаетесь из другого потока изменить свойство какого-либо компонента в форме. Как известно, VCL однопоточный, весь код внутри программы выполняется последовательно. Допустимо, в процессе работы изменились какие-то данные внутри классов VCL, система отбирает время у основного потока, передает по кругу другим потокам и возвращает назад, при этом выполнение кода продолжается с того места, где прекратилось. Если мы из своего потока что-то изменяем, например, на форме, задействовано много механизмов внутри VCL (напомним, выполнение основного потока пока "прекращено"), соответственно за это время успеют измениться любые данные. И здесь вдруг время опять отдается основному потоку, он спокойно продолжает свое выполнение, но даны уже изменены! К чему это может привести - предусмотреть нельзя. Вы можете проверить это тысячу раз, и ничего не случится, а на тысячу первый программа свалится. И это относится не только к взаимодействию дополнительных потоков с главным, но и к взаимодействию потоков между собой. Писать такие ненадежные программы обычно нельзя.
Дата добавления: 2016-02-27; просмотров: 742;