Многопото́чность (Wiki) — свойство платформы (например, операционной системы, JVM и т. д.) или приложения, состоящее в том, что процесс, порождённый в операционной системе, может состоять из нескольких потоков, выполняющихся «параллельно», то есть без предписанного порядка во времени. При выполнении некоторых задач такое разделение может достичь более эффективного использования ресурсов вычислительной машины.
Чаще всего используется при работе с интернетом.
В этом уроке я расскажу как ПРАВИЛЬНО реализовать многоточность в программе. (Кто-то реализует ее иначе, но ИМХО ниже приведенный способ является самым рациональным)
Использовать будем Embarcadero Rad Studio 2010 (2009).
Создадим новый юнит - Thread Object
[Ссылки могут видеть только зарегистрированные пользователи. ]
в имени класса напишем TNewThread
Теперь у нас есть заготовка потока. Сохраним этот файл, как newthread.pas
Вернемся к главной форме. Кинем на форму Memo (переименуем в Code) и Button (Button1).
[Ссылки могут видеть только зарегистрированные пользователи. ]
в uses основной программы добавим наш "потоковый" юнит - newthread
Тыкнем 2 раза на кнопку и в обработчике пропишем одиночный вызов потока:
Код:
procedure TForm1.Button1Click(Sender: TObject);
begin
TNewThread.create(false);
end;
При создании потока передается логическая переменная. Она отвечает за запуск потока (либо сразу же выполнить код, либо создать поток и ждать команды "выполнить код"). В нашем случае false значит выполнить код сразу после запуска.
Вернемся в поток и добавим после implementation
Код:
uses *имя основного файла*;
а в процедуру Execute следующий код:
Код:
procedure Tnewthread.Execute;
begin
form1.Code.Text:='Thread Created!';
self.Terminate;
end;
Запустим программу и нажмем на кнопку. Если тект в поле изменился на Thread Created! значит мы все сделали правильно и можно поздравить себя с успешным запуском потока)
А теперь научимся производить различные действия с потоками, передавать в них дополнительные переменные, вызывать нужно кол-во и контролировать его. А также синхронизировать потоки.
Для начала разберем передачу дополнительных данных в поток. Например прокси или адрес страницы.
Для этого добавим в type потока
Код:
public
constructor Create(CreateSuspended: boolean; uid1:string);
CreateSuspended: boolean - та самая переменная, ее необходимо прописать, если будем писать свой "конструктор".
uid1:string - наша дополнительная переменная. Кроме нее добавим в private uid:string; как глобальную переменную для этого потока и переменную nick:string;
uid := uid1; - передаем переменную в глобальную. Что такое Synchronize(inc1); я объясню позже.
Теперь усложним наш код потока.
В uses добавим idhttp
А процедура Execute будет выглядеть следующим образом:
Код:
procedure Tnewthread.Execute;
var
http: tidhttp;
s: string;
i: integer;
begin
try
nick := '';
s:='';
http := tidhttp.Create;
http.HandleRedirects := true;
http.request.useragent :=
'mozilla/3.0 (compatible; msie 7.0; windows nt 5.0)';
http.request.acceptencoding := 'gzip, deflate';
http.ReadTimeout := 30000;
http.ConnectTimeout := 30000;
try
s := http.Get('http://exphack.ru/member.php?u='+uid);
except
end;
finally
try
if length(s) <> 0 then
begin
if pos('Просмотр профиля: ', s) > 0 then
begin
for i := pos('Просмотр профиля: ', s) + 18 to length(s) do
if s[i] = '<' then
break
else
nick := nick + s[i];
if length(nick) > 0 then
Synchronize(UpdateGood)
else
Synchronize(UpdateBad);
end
else
Synchronize(UpdateBad);
end
else
Synchronize(UpdateError);
except
end;
Synchronize(dec1);
self.Terminate;
end;
end;
Начнем разбор.
1 это обнуление глобальной переменной. (В нашем случае обязательно).
2 это, как вы уже догадались парсер форума exphack.ru да и вообще буллетина. Работа с инди думаю понятна, если непонятна - гуглим.
Рассмотрим финал процедуры:
Здесь мы вытаскиваем из полученного кода страницы наш ник. А что же за странные строки с Synchronize?
Это функция вызывающаяся для другой процедуры. Она обеспечивает "очередность" потоков - не дает одновременно нескольким потокам обращатся к переменной. Это важно при использовании таких функций, как inc и dec. Дело в том, что эти функции выполняются в 3 этапа на машинном уровне и если одновременно несколько потоков вызовут их для одной переменной (допустим переменной из основного юнита, отвечающей за кол-во активных потоков) может случится что выскочит лишний +1 или -1.
Соответственно код процедур inc1 и dec1:
Код:
procedure Tnewthread.inc1;
begin
inc(tpot);
end;
procedure Tnewthread.dec1;
begin
dec(tpot);
end;
где tpot - глобальная переменная основного юнита, отвечающая за число активных потоков.
Тоесть - при запуске потока она увеличивается на 1, а при завершении уменьшается на 1. Обязательно прописывайте inc1 в конструкторе, а не в Execute!
Ну а процедуры Update отвечают за вывод нужных результатов куда надо (файлы, поля, количественные переменные и т д)
Не забудьте что все эти процедуры необходимо прописать в protected:
Теперь наш поток полностью готов. Но необходимо еще и контролировать их кол-во.
Для этого я использую следующий метод их вызова и ожидания:
В основном потоке при нажатии кнопки происходит:
Код:
k:=*начальный уид*;
maxuid:=*конечный уид*;
maxthreads:=*кол-во потоков*;
tpot:=0;
while k<=maxuid do
begin
application.processmessages();
if tpot = maxthreads then
while tpot = maxthreads do
begin
sleep(10);
application.processmessages();
end;
Tnewthread .Create(false, inttostr(k));
inc(k);
end;
Ну и часто надо дождаться пока завершаясь ВСЕ потоки:
Код:
while tpot > 0 do
begin
sleep(10);
application.processmessages();
end;
Выкладываю код потока: [Ссылки могут видеть только зарегистрированные пользователи. ]
P.S Для корректной работы с интернетом необходимы библиотеки Indy ПОСЛЕДНЕЙ версии!