Потоки в windows на win-api, псевдомногозадачность
В статье мы поговорим о потоках в windows, и их реализации на win-api. Для начала, расскажу
вообще, что такое поток и для чего он используется. Давным давно, на компьютерах
стояли такие операционные системы как dos. Одной из досовских особеннойстей является однозадачность.
То есть процессор выполнял одну программу, и лишь после её завершения, мог преступить к следующей.
Налицо огромное количество недостатков, и перечислять их все нет смысла. На современном однопроцессорном компьютере
(здесь я взял однопроцессорный, так как именно однопроцессорные мейнфреймы стоят у большинства людей дома) используется такая
технология как псевдомногозадачность. Работает она так: процессор, тратит на каждую программу n-ое количество времени,
практически, при таком подходе достигается многозадачность, т.к. одновременно работают все программы, и на каждую процессор
затрачивает немного своего времени... Или много. Тут мы внесём новое понятие - приоритет процесса. Именно этой
гайкой мы регулируем сколько процессор выполняет каждый процесс. Предположим у нас работает два процесса. Пока у них
одинаковый приоритет - процессор затрачивает на них одинаковое время - к примеру по 500 миллисекунд. Однако, изменим
приоритет одному из процессов, например на более высокий, и процессу с более высоким приоритетом будет выделяться
скажем, 700 миллисекунд, а второму - всего навсего оставшиеся 300.
Шагаем далее - а что же, если нашей программе тоже требуется многозадачность? Например, мы выводим линию по пикселу в цикле,
предположим у нас два цикла, которые рисуют две разных линии. Что будет при обычном раскладе - сначала выполниться первый цикл, и окончившись,
начнётся второй. А если нам нужно, что бы линии выводились синхронно? Или, нужно например произвести огромные расчёты,
при этом не мешая пользователю работать с программой? Вот тут-то мы и познакомимся с понятием ...
* Здесь под общий свист и апплодисменты заходит поток.
Да-да, потока, того самого, что всегда нас выручит в такой ситуации. Итак поток - это несколько независимых блоков кода, которые могут
выполняться обсолютно одновременно. Таким образом мы получаем многозадачность внутри нашей собственной программы! Применений море,
можно даже не приводить примеров, думаю твоё воображение придумает достойное применение этому =)
Здесь начинается сам хардкор. За кодинг!
Первым делом втыкай в розетку свой delphi, создавай обычный проект, и удаляй там весь бред, который насоздавала дельфи (и почемуй-то
рисуется она у меня женского рода =) - а именно Project -> Remove from project и сноси оттуда unit1. Теперь заходи в Project -> View Source.
Там тоже много бреда, удаляй все строки оттуда и вставь эти:
program HiThreads;
uses
Windows;
begin
end.
С такого немногословного кода мы начнём нашу программу. Продолжим - для начала приведи всё к такому виду:
program HiThreads;
uses
Windows;
procedure MyFunct;
var
i : integer;
dc : HDC;
begin
dc := GetDc(0);
for i := 0 to 1000 do
begin
SetPixel(dc, 10, i, 255);
Sleep(10);
end;
DeleteDc(dc);
end;
procedure MyFunct2;
var
i : integer;
dc : HDC;
begin
dc := GetDc(0);
for i := 0 to 1000 do
begin
SetPixel(dc, 20, i, 255);
Sleep(10);
end;
DeleteDc(dc);
ExitProcess(0);
end;
begin
MyFunct;
MyFunct2;
end.
Здесь программа, состоит она из двух простеньких и почти одинаковых процедур, выводящих
параллельные линии на скрине твоего монитора и последующего их вызова. Скажу лишь, что
сначала выводиться одна линия, и лишь затем - вторая. А теперь подарок в студию! Испробовав только написанный нами
код, пиши новый:
program ProjThread;
uses
Windows;
var
sa, sa2 : SECURITY_ATTRIBUTES;
idsr : Cardinal;
idsr2 : Cardinal;
msg : TMsg;
ht, ht2 : THandle;
procedure MyFunct;
var
i : integer;
dc : HDC;
begin
dc := GetDc(0);
for i := 0 to 1000 do
begin
SetPixel(dc, 10, i, 255);
Sleep(10);
end;
DeleteDc(dc);
end;
procedure MyFunct2;
var
i : integer;
dc : HDC;
begin
dc := GetDc(0);
for i := 0 to 1000 do
begin
SetPixel(dc, 20, i, 255);
Sleep(10);
end;
DeleteDc(dc);
ExitProcess(0);
end;
begin
ht := CreateThread(@sa, 0, @MyFunct, nil, 0, idsr);
ht2 := CreateThread(@sa2, 0, @MyFunct2, nil, 0, idsr2);
SetThreadPriority(ht, THREAD_PRIORITY_HIGHEST);
SetThreadPriority(ht2, THREAD_PRIORITY_LOWEST);
while GetMessage(msg, 0, 0, 0) do
begin
TranslateMessage(msg);
DispatchMessage(msg);
end;
end.
Поясню код. Здесь всё те же процедуры, что и в прошлой программе, однако обрати внимание, как вызываются процедуры.
Перво-наперво делаем так:
ht := CreateThread(@sa, 0, @MyFunct, nil, 0, idsr);
ht2 := CreateThread(@sa2, 0, @MyFunct2, nil, 0, idsr2);
Именно эти строки создают волшебные потоки, при этом линии будут рисоваться абсолютно одновременно.
И всё же - вторая линия немного "отстаёт" от первой, что ж такое? Нет, вторая не курит, а первая не занимается лёгкой атлетикой,
просто, опустимся на шаг ниже и видим:
SetThreadPriority(ht, THREAD_PRIORITY_HIGHEST);
SetThreadPriority(ht2, THREAD_PRIORITY_LOWEST);
Именно эти строки устанавливают описанный выше приоритет потока. Да, теперь
понятно,(людям с хотя бы небольшим знанием английского) ведь у второго потока приоритет
установлен на низкий, в отличие от первого, которого мы наделили большой властью -
THREAD_PRIORITY_HIGHEST - высокий потоковый приоритет. Попробуй при работе нашей программы
позапускать разные "увесистые" софтинки - например winamp с новомодным скином =) И увидишь, как
вторая линия отстаёт всё больше и больше...
Теперь, поговорим об управлении потоками.
Функция GetThreadPriority возвращает приоритет потока, ей нужно передать один параметр - дескриптор потока.
Функция TerminateThread уничтожает поток. Передаём ей два параметра - дескриптор процесса и код завершения,
в штатных ситуациях вбивай этот параметр 0.
Функция CloseHandle убивает дескриптор. Любой, ей пофиг, ей только передай в качестве единственного параметра
сам дескриптор, как она его сразу того... "закроет" =)
Ну теперь поговорим о вопросе удаления потоков и дескрипторов. В программе, представленной выше,
код убийства всего и вся, с очищением памяти, будет такой:
TerminateThread(ht, 0);
TerminateThread(ht2, 0);
CloseHandle(ht);
CloseHandle(ht2);
Если добавить этот код после установки приоритетов потоков, то после запуска, потоки
будут убиты, и программа просто "зависнет" в памяти навсегда (в цикле while GetMessage...), так и не нарисовав ничего.
Вот и всё! Надеюсь тебе понравился короткий экскурс в мир потоков =)
SLAyer
Другие статьи
По всем вопросам и предложениям по сайту пишите на info@comp-info.ru  
|