HOME FORUMS MEMBERS RECENT POSTS LOG IN  
× Авторизация
Имя пользователя:
Пароль:
Нет аккаунта? Регистрация
Баннер 1   Баннер 2
НОВЫЕ ТОРГОВАЯ НОВОСТИ ЧАТ
loading...
Скрыть
Вернуться   ANTICHAT > ИНФО > Статьи
   
Ответ
 
Опции темы Поиск в этой теме Опции просмотра

  #1  
Старый 29.08.2012, 22:05
shadowrun
Постоянный
Регистрация: 29.08.2010
Сообщений: 840
С нами: 8265206

Репутация: 84


По умолчанию



[Предисловие]​


В своей практике частенько встречаюсь с необходимостью написания многопоточных приложений. Вот решил поделиться опытом и показать основные краеугольные камни синхронизации потов. Было у меня время, когда при упоминании многопоточности я обливался холодным потом, а мое анальное отверстие сжималось с такой силой, что могло спокойно перекусить металлический лом, или кусок арматуры. У большинства начинающих кодеров, которые впервые слышат о потоках ощущения примерно те-же. Вот для вас господа я то и стараюсь . Предупреждаю сразу: я не експерт и не гуру кодинга, все что буду здесь писать основано на личном опыте. Если будут критические замечания или дельные советы, судовольствием добавлю их в сей текст.



[Нах оно нужно?]​


Вообще-то статейка направлена на кодеров, которые уже в курсе что такое потоки и как создавать поток. Но все-же скажу пару мотивирующих слов... Основное преимущество многопоточного приложения - это скорость выполнения работы. Я преимущественно имею дело с приложениями, которые работают через вэб интерфейсы и по сравнению с однопоточными, приложения работающие в несколько потоков по скорости выигрывают в разы. Ну воды уже налил достаточно, подошло дело к практике .



[Кодинг]​


ТЗ: Создать многопоточный парсер имени и фамилии социальной сети ВКонтакте по списку ID.

Своей извращенной фантазией я представил данное приложение в таком виде:


1 - Поле мемо (DigitLst) со списком чисел ID.

2 - Поле мемо (DataLst) со списком спаршенных имен.

3 - Для генерации чисел набросал простенькую процедурку:

PHP код:
[COLOR="#000000"][COLOR="#0000BB"]procedure TMainFrm[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Button1Click[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]Sender[/COLOR][COLOR="#007700"]:[/COLOR][COLOR="#0000BB"]TObject[/COLOR][COLOR="#007700"]);

var[/
COLOR][COLOR="#0000BB"]I[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]J[/COLOR][COLOR="#007700"]:[/COLOR][COLOR="#0000BB"]integer[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]S[/COLOR][COLOR="#007700"]:[/COLOR][COLOR="#0000BB"]String[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]begin

Randomize
[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]DigitLst[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Clear[/COLOR][COLOR="#007700"];

for[/COLOR][COLOR="#0000BB"]I[/COLOR][COLOR="#007700"]:=[/COLOR][COLOR="#0000BB"]1 to StrToInt[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]Edit2[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Text[/COLOR][COLOR="#007700"]) do

[/COLOR][COLOR="#0000BB"]begin

[/COLOR][COLOR="#007700"]for[/COLOR][COLOR="#0000BB"]J[/COLOR][COLOR="#007700"]:=[/COLOR][COLOR="#0000BB"]1 to StrToInt[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]Edit1[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]text[/COLOR][COLOR="#007700"]) do

[/COLOR][COLOR="#0000BB"]S[/COLOR][COLOR="#007700"]:=[/COLOR][COLOR="#0000BB"]S[/COLOR][COLOR="#007700"]+[/COLOR][COLOR="#0000BB"]IntToStr[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]Random[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]10[/COLOR][COLOR="#007700"]));

[/
COLOR][COLOR="#0000BB"]DigitLst[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Lines[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Add[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]S[/COLOR][COLOR="#007700"]);

[/
COLOR][COLOR="#0000BB"]S[/COLOR][COLOR="#007700"]:=[/COLOR][COLOR="#DD0000"]''[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]end[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]SL[/COLOR][COLOR="#007700"]:=[/COLOR][COLOR="#0000BB"]TStringList[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Create[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]SL[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]AddStrings[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]DigitLst[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Lines[/COLOR][COLOR="#007700"]);

[/
COLOR][COLOR="#0000BB"]end[/COLOR][COLOR="#007700"];[/COLOR][/COLOR
сильно углубляться не стану, скажу лишь, что значение Edit2.Text отвечает за количество сгенерированных чисел а Edit1.text за количество цифр в числе. Прошу обратить внимание на строки

SL := TStringList.Create;

SL.AddStrings(DigitLst.Lines);

Я создаю список SL и построчно подгружаю в него значение нашего мемо со сгенерированными числами (DigitLst.Lines). Дело в том, что с этим списком мы будем работать из потоков, а так как мемо VCL компонент я не хочу загромождать код дополнительными моментами синхронизации.

Собственно прежде чем перейдем к созданию потока, попрошу уяснить одну простую вещь:

СИНХРОНИЗАЦИЯ - ЭТО ОЧЕНЬ ПРОСТО!


Давайте рассмотрим алгоритм работы потока:

1 - взять определенный Id из SL.

2 - удалить выбранный Id, дабы другие потоки не получили его значение.

3 - Отправить запрос вконтактику, получить ответ, спарсить необходимый результат.

4 - Записать полученный результат в мемо на форме

5 - если в списке нет строк - закончить работу.

Из этого списка могут вызвать траблы пункт 1,2,4. Проблемы с пунктами 1,2 могут возникнуть в том случае, если несколько потоков одновременно возьмут/удалят одно и то-же значение из списка. В пункте 4 чревато обращение нескольких потоков к компоненту VCL. Как избежать этих проблем? Нужно просто усвоить некоторые правила работы с потоками:



1. Переменная в которой содержаться данные используемые потоками должна быть глобальной.

2. Чтобы избежать проблем при работе с глобальными переменными используются критические секции TCriticalSection.

3. Для работы с глобальными переменными непосредственно в потоке нужно использовать поля класса потока (переменные объявленные в разделе private).

4. Если поток обращается к любому визуальному компоненту необходимо использовать процедуру Synchronize, аргументом которой будет процедура обращения к визуалу без параметров.



PHP код:
[COLOR="#000000"][COLOR="#0000BB"]unit Unit2[/COLOR][COLOR="#007700"];

interface

[/
COLOR][COLOR="#0000BB"]uses

Classes
[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]IdHttp[/COLOR][COLOR="#007700"];

[/COLOR][COLOR="#0000BB"]type

ParsThrd
[/COLOR][COLOR="#007700"]= class([/COLOR][COLOR="#0000BB"]TThread[/COLOR][COLOR="#007700"])

private

[/COLOR][COLOR="#0000BB"]FCurrLnk[/COLOR][COLOR="#007700"]:[/COLOR][COLOR="#0000BB"]String[/COLOR][COLOR="#007700"];[/COLOR][COLOR="#FF8000"]// поле, доступно данному потоку для хранения значения текущего ID

[/COLOR][COLOR="#0000BB"]Ftmp[/COLOR][COLOR="#007700"]:[/COLOR][COLOR="#0000BB"]String[/COLOR][COLOR="#007700"];[/COLOR][COLOR="#FF8000"]//поле для передачи данных VCL объекту

[/COLOR][COLOR="#007700"]protected

[/
COLOR][COLOR="#0000BB"]procedure Execute[/COLOR][COLOR="#007700"];[/COLOR][COLOR="#0000BB"]override[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]procedure UpdMemo[/COLOR][COLOR="#007700"];[/COLOR][COLOR="#FF8000"]// метод без параметров для процедуры Synchronize

[/COLOR][COLOR="#0000BB"]end[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]implementation

uses Unit1
[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]IdHTTPHeaderInfo[/COLOR][COLOR="#007700"];

var[/
COLOR][COLOR="#0000BB"]DoWork[/COLOR][COLOR="#007700"]:[/COLOR][COLOR="#0000BB"]Boolean[/COLOR][COLOR="#007700"]=[/COLOR][COLOR="#0000BB"]True[/COLOR][COLOR="#007700"];[/COLOR][COLOR="#FF8000"]// переменная которая оборвет цикл, если в списке с ID закончатся строки.

[/COLOR][COLOR="#0000BB"]procedure ParsThrd[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]UpdMemo[/COLOR][COLOR="#007700"];[/COLOR][COLOR="#FF8000"]// описание метода записывающего результат в мемо формы.

[/COLOR][COLOR="#0000BB"]begin

MainFrm
[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]DataLst[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Lines[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Add[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]Ftmp[/COLOR][COLOR="#007700"]);

[/
COLOR][COLOR="#0000BB"]end[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]procedure ParsThrd[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Execute[/COLOR][COLOR="#007700"];

var[/
COLOR][COLOR="#0000BB"]Http[/COLOR][COLOR="#007700"]:[/COLOR][COLOR="#0000BB"]TIdhttp[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]ST[/COLOR][COLOR="#007700"]:[/COLOR][COLOR="#0000BB"]TStringStream[/COLOR][COLOR="#007700"];[/COLOR][COLOR="#FF8000"]// Переменная в которую будет писаться ответ сервера, дабы не было проблем с русскими буквами.

[/COLOR][COLOR="#0000BB"]begin

[/COLOR][COLOR="#007700"]while[/COLOR][COLOR="#0000BB"]DoWork[/COLOR][COLOR="#007700"]=[/COLOR][COLOR="#0000BB"]True[/COLOR][COLOR="#007700"]do[/COLOR][COLOR="#FF8000"]//зацикливаем работу потока

[/COLOR][COLOR="#0000BB"]begin

[/COLOR][COLOR="#FF8000"]//CS: TCriticalSection;

//ниже мы используем критическую секцию.

// Код расположен в блоке CS.Enter CS.Leave не будет сеиваться между потоками.

// Здесь мы проверяем на наличие строк в списке, присваиваем значение строки полю FCurrLnk и удаляем эту строку из списка.

// Если не использовать крит. секцию то между потоками возникнет своеобразный хаос,

// с нежелательными последствиями

[/COLOR][COLOR="#0000BB"]CS[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Enter[/COLOR][COLOR="#007700"];

if[/COLOR][COLOR="#0000BB"]SL[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Count[/COLOR][COLOR="#007700"]<>[/COLOR][COLOR="#0000BB"]0

then

begin

[/COLOR][COLOR="#007700"]try

[/
COLOR][COLOR="#0000BB"]FCurrLnk[/COLOR][COLOR="#007700"]:=[/COLOR][COLOR="#0000BB"]Sl[/COLOR][COLOR="#007700"][[/COLOR][COLOR="#0000BB"]0[/COLOR][COLOR="#007700"]];

[/
COLOR][COLOR="#0000BB"]SL[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Delete[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]0[/COLOR][COLOR="#007700"]);

finally

[/COLOR][COLOR="#0000BB"]CS[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Leave[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]end

[/COLOR][COLOR="#007700"]else

[/
COLOR][COLOR="#0000BB"]begin

CS
[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Leave[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]DoWork[/COLOR][COLOR="#007700"]:=[/COLOR][COLOR="#0000BB"]False[/COLOR][COLOR="#007700"];

Exit;

[/
COLOR][COLOR="#0000BB"]end[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#FF8000"]// На данном участке кода формируем более-менее приемлимые заголовки, отсылаем запрос и парсим результат.

[/COLOR][COLOR="#0000BB"]Http[/COLOR][COLOR="#007700"]:=[/COLOR][COLOR="#0000BB"]TIdHTTP[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Create[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]nil[/COLOR][COLOR="#007700"]);

[/
COLOR][COLOR="#0000BB"]with Http[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Request[/COLOR][COLOR="#007700"]do

[/
COLOR][COLOR="#0000BB"]begin

UserAgent
[/COLOR][COLOR="#007700"]:=[/COLOR][COLOR="#DD0000"]'Mozilla/5.0 (Windows NT 5.1; rv:14.0) Gecko/20100101 Firefox/14.0.1'[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]Host[/COLOR][COLOR="#007700"]:=[/COLOR][COLOR="#DD0000"]'vk.com'[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]AcceptLanguage[/COLOR][COLOR="#007700"]:=[/COLOR][COLOR="#DD0000"]'ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3'[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]ContentType[/COLOR][COLOR="#007700"]:=[/COLOR][COLOR="#DD0000"]'text/html; charset=windows-1251'[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]end[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]ST[/COLOR][COLOR="#007700"]:=[/COLOR][COLOR="#0000BB"]TStringStream[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Create[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#DD0000"]''[/COLOR][COLOR="#007700"]);

try

[/COLOR][COLOR="#0000BB"]Http[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Get[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#DD0000"]'http://vk.com/id'[/COLOR][COLOR="#007700"]+[/COLOR][COLOR="#0000BB"]FCurrLnk[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]ST[/COLOR][COLOR="#007700"]);

[/
COLOR][COLOR="#0000BB"]Http[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Disconnect[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]Ftmp[/COLOR][COLOR="#007700"]:=[/COLOR][COLOR="#0000BB"]ST[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]DataString[/COLOR][COLOR="#007700"];

finally

[/COLOR][COLOR="#0000BB"]http[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Free[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]ST[/COLOR][COLOR="#007700"].[/COLOR][COLOR="#0000BB"]Free

end
[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]Ftmp[/COLOR][COLOR="#007700"]:=[/COLOR][COLOR="#0000BB"]Copy[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]Ftmp[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]Pos[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#DD0000"]''[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]Ftmp[/COLOR][COLOR="#007700"]) +[/COLOR][COLOR="#0000BB"]7[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]Pos[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#DD0000"]''[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]Ftmp[/COLOR][COLOR="#007700"]) -[/COLOR][COLOR="#0000BB"]Pos[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#DD0000"]''[/COLOR][COLOR="#007700"],[/COLOR][COLOR="#0000BB"]Ftmp[/COLOR][COLOR="#007700"]) -[/COLOR][COLOR="#0000BB"]7[/COLOR][COLOR="#007700"]);

[/
COLOR][COLOR="#0000BB"]Synchronize[/COLOR][COLOR="#007700"]([/COLOR][COLOR="#0000BB"]UpdMemo[/COLOR][COLOR="#007700"]);[/COLOR][COLOR="#FF8000"]// Синхронизируем обращение потока к мемо.

[/COLOR][COLOR="#0000BB"]end[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]end[/COLOR][COLOR="#007700"];

[/
COLOR][COLOR="#0000BB"]end[/COLOR][COLOR="#007700"].[/COLOR][/COLOR
Код постарался привести к максимально читабельному виду. Если что не понятно, спрашивайте, буду рад ответить.



[Эпилог]​


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

____________

Особая благодарность за консультации и поддержку: DooD , mironich



софт + сурсы
 
Ответить с цитированием

  #2  
Старый 29.08.2012, 22:11
mironich
Постоянный
Регистрация: 27.02.2011
Сообщений: 733
С нами: 8003126

Репутация: 19


По умолчанию

Цитата:
Сообщение от None  
Http := TIdHTTP.Create(nil);
Лучше раз и в конструкторе, чем каждый раз создавать.

А так я рад что ты таки написал ее.

ЗЫ есче всегда выходить из крит секции надо в try: finally: иначе искл. поймаем и не выйдем.
 
Ответить с цитированием

  #3  
Старый 30.08.2012, 11:06
shadowrun
Постоянный
Регистрация: 29.08.2010
Сообщений: 840
С нами: 8265206

Репутация: 84


По умолчанию

Цитата:
Сообщение от mironich  
ЗЫ есче всегда выходить из крит секции надо в try: finally: иначе искл. поймаем и не выйдем.
Спасибо, добавил.
 
Ответить с цитированием
Ответ



Предыдущая тема Следующая тема

Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT ™ © 2001- Antichat Kft.