Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Проблемы с потоками при закачке страниц


Страницы:   1 2 3 4 5

Pcrepair ©   (2012-12-27 11:20:00) 

Добрый день.
Есть функция вида function LoadPage(const Url:string):string; для закачки страниц на базе idHHP.Get, которая обеспечивает:
- закачку страниц с фреймами
- закачку страниц по HTTPs
- смену Request.UserAgent (доп. функция) по Random из списка агентов
- подавление исключений в коде idHHP.Get с записью в лог причин
- вывод сообщений о утечке памяти при закрытии Арр
Сама по себе функция работает нормально, загружает все страницы и без утечки памяти.

Есть код запуска этой функции в многопоточном режиме:

unit Main;
....
implementation
{$R *.dfm}
uses uLoadPage,uGlobalVar;

(*нажимает эту кнопку и понеслось*)
procedure TMainForm.Btn_StartLoadURLsClick(Sender: TObject);
var
UrlList:TStringList; (*эквивалент передаваемого массива строк из ПМ*)
i:integer;
Url:string; (*строка = УРЛ из массива*)
begin
  UrlList:=TStringList.Create;
  UrlList.Text:=Mm_InsSMUrls.Text; (*ввод массива строк УРЛ*)
    (*----ЦИКЛ мульти закачки страниц по числу УРЛ в Mm_InsSMUrls---*)
    for i := 0 to UrlList.Count-1 do
      begin
         Url:=UrlList[i]; (*передача УРЛ*)
         Loader(Url); (*вызов процедуры запуска потока закачки страницы*)
         Sleep(10);  (*страховка от перегрева процессора а также от толпы потоков, которые мешают друг другу?*)
      end;
  FreeAndNil(UrlList);
end;


unit uLoadPage;
interface
uses SysUtils, uThreadLoader, Classes;
Procedure Loader(Url:string);
implementation
Procedure Loader(Url:string);
var ThreadLdr: TLoader;
begin
   ThreadLdr:=Tloader.Create(True);
   ThreadLdr.Priority:=tpNormal;  
   ThreadLdr.FreeOnTerminate:=True;
   ThreadLdr.Url:=Url;
   ThreadLdr.Start;
 end;
end.


unit uThreadLoader;
interface
uses
 System.Classes, idHTTP, SysUtils, Dialogs;
type
 TLoader = class(TThread)
 private
    FUrl:string;
    FHtml:string;
   { Private declarations }
 protected
   procedure Execute; override;
   procedure CallStore;
   function LoadHtmlCode(const Url:string):string;
 public
   property Url: string read FUrl write FUrl;  (*свойство класа, доступное для всей программы*)
   property Html: string read FHtml write FHtml;
 end;

implementation
uses uPageLoader, uLoadPage, uGlobalVar;

function TLoader.LoadHtmlCode(const Url:string):string;
begin
 Result:=LoadPage(Url); (*вызываем функцию загрузки страницы из uPageLoader*)
end;

procedure TLoader.CallStore;
begin
  gStore.Add(Html); (*ввод новой строки в ГлобПерем*)
end;

procedure TLoader.Execute;
begin
  Html:=LoadHtmlCode(Url);  (*загрузка страницы в Поле Класса(свойство)*)
   CS.Enter;  (*вход в КС*)
      CallStore; (*передаем результат в ГП*)
   Cs.Leave;  (*выход из КС*)
end;

end.


unit uPageLoader;
interface
uses Classes, SysUtils, idHTTP, StrUtils, IdSSL, IdSSLOpenSSL;
   function LoadPage(const Url:string):string;  (*Основная функция Юнита*)
   procedure ErrorLog(Url:string);
   function LoadHtmlPage(const PageUrl:string):string;
   function DelJS(const Code:string):string;
   function RestoreFrame(const Code:string):string;
   function RestoreFullUrl(const Code:string; const RootUrl:string):string;
   function GetRandomUserAgent: string;
   var
   Loader:TidHTTP;
   SSL:TIdSSLIOHandlerSocketOpenSSL;

implementation
тут много чего, но все работает, смотри в начало
end.


unit uGlobalVar;
interface
uses Classes, SysUtils, SyncObjs;
var
gStore: TStringList=nil; (*ГП для хранения закачанных страниц*)
CS:TCriticalSection; (*используем КС для передачи результата в основной поток*)
implementation

initialization
  gStore:=TStringList.Create;
  gStore.Duplicates:=dupAccept;
  gStore.Sorted:=False;
  Cs:=TCriticalSection.Create;
finalization
 FreeAndNil(gStore);
 FreeAndNil(CS);
end.


Результат:
В режиме отладки видно что возникает множество самых различных ошибок:
- First chance exception at $00407466. Exception class $C0000005 with message 'access violation at 0x00407466: read of address 0x000000a4'. Process Loader_VER06.exe (2012)
- First chance exception at $7C812AFB. Exception class EIdHTTPProtocolException with message 'Set-Cookie: dle_password=deleted; expires=Tue, 27-Dec-2011 20:37:50 GMT; path=/; domain=.freeseller.ru; httponly'. Process Loader_VER06.exe (2012)
First chance exception at $00629F8C. Exception class $C0000005 with message 'access violation at 0x00629f8c: read of address 0x00000148'. Process Loader_VER06.exe (2012)
- First chance exception at $7C812AFB. Exception class EOSError with message
'System Error.  Code: 123. Синтаксическая ошибка в имени файла, имени папки или метке тома'.
Process Loader_VER06.exe (2012)
- First chance exception at $7C812AFB. Exception class EIdNotASocket with message
'Socket Error # 10038
Socket operation on non-socket.'.
Process Loader_VER06.exe (2012)

В рабочем режиме, за счет подавления исключений, все это конечно не вылазит, но:
- закачиваются не все страницы(хотя все УРЛ валидные и функцией закачки закачиваются без вопросов)
- после закрытия Арр выводится сообщение о многочисленных маленьких утечках памяти

По теории потоки не должны пересекаться. каждый имеет свою память. вывод результата через КС. такое впечетление что потоки как то мешают друг другу
Внимание!!! Вопрос: в чем может быть причина появления исключений и утечки памяти именно в режиме многопотоковой закачки?


DVM   (2012-12-30 23:55:00) [21]


> Pcrepair ©   (30.12.12 23:48) [79]


> и какую же кодировку указать?


> оне часто не совпадают

Более того они обе могут быть и неправильными. А ты от Indy хочешь чтоб она правильно перекодировала.

Вообще сначала надо смотреть кодировку страницы и если она не указана то брать кодировку сервера


Pcrepair   (2012-12-30 23:48:00) [22]

и какую же кодировку указать? ту что в заголовке страницы, или ту что получена от сервера? оне часто не совпадают


DVM   (2012-12-30 23:41:00) [23]


> Pcrepair ©   (30.12.12 23:37) [76]


> вот если использовать Tmemory.Stream для вывода кода закачанной
> страницы(idHttp>Get(Url, Stream), там действительно кракозябры
> вместо кырилицы, поскольку поток выводит код в первозданном
> виде

Вот именно, что в первозданном, если эту страницу надо сохранить где то, то ее в этом первозданном виде и надо сохранять, а до кучи и хидеры HTTP к ней. А перед использованием приводить к читабельному виду с помощью TStringStream с указанием кодировки например. По крайней мере так ничего не теряется от оригинала.


DVM   (2012-12-30 23:41:00) [24]


> Pcrepair ©   (30.12.12 23:37) [76]


> вот если использовать Tmemory.Stream для вывода кода закачанной
> страницы(idHttp>Get(Url, Stream), там действительно кракозябры
> вместо кырилицы, поскольку поток выводит код в первозданном
> виде

Вот именно, что в первозданном, если эту страницу надо сохранить где то, то ее в этом первозданном виде и надо сохранять, а до кучи и хидеры HTTP к ней. А перед использованием приводить к читабельному виду с помощью TStringStream с указанием кодировки например. По крайней мере так ничего не теряется от оригинала.


Pcrepair   (2012-12-30 23:37:00) [25]

а исключений не было. программа просто зависала и все. что в режиме отладки что откомпилилированная.

и еще раз повторю: похоже инди 10.5.9.0 УЖЕ имеет отношение к выбору кодировки страницы - видимо встроили. вот если использовать Tmemory.Stream для вывода кода закачанной страницы(idHttp>Get(Url, Stream), там действительно кракозябры вместо кырилицы, поскольку поток выводит код в первозданном виде. а если idHttp.Get(Url), кырилицы в норме(имеется ввиду закачка одной и той же страницы). у меня во всяком случае так. специально проверял


bems   (2012-12-30 23:30:00) [26]


> Так вроде не по стандарту же.

я ж как чукча, что вижу о том и пою. И не говорю что это должно корректно обрабатываться


> возмущение Indy резонно.

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


DVM   (2012-12-30 23:30:00) [27]


> Pcrepair ©   (30.12.12 23:22) [71]


> может ИНДИ(из комплекта ХЕ2) и огого, но вот спотыкалась
> при загрузке некоторых страниц, причем так спотыкалась что
> даже полное подавление исключений не помогло - программа(простейший
> код) просто зависала и все. а в РАД2010 и ХЕ3 тот же самый
> код работает нормально.

И что там в исключениях написано было?


> что касается кодовой страницы: в РАД2010 - кракозябры, в
> ХЕ3 - кырилица в норме( в 80%) случаев. так что выводы сделать
> можно и без огромного опыта

Еще раз повторю, TIdHTTP не имеет никакого отношения к кодировкам. То, что у метода Get есть вариант возвращающий строку, это еще не значит что он обязан за тебя делать перекодировку. И вообще, надо использовать вариант с Get где TStream в параметрах, получать кодировку документа и перекодировать полученные данные.


DVM   (2012-12-30 23:25:00) [28]


> Pcrepair ©   (30.12.12 23:22) [71]
> может тогда уже использовать TMultiReadExclusiveWriteSynchronizer?
>  чем писать свои реализации чтение-запись?

с TMultiReadExclusiveWriteSynchronizer придется писать ровно то же что и с крит секцией.


DVM   (2012-12-30 23:23:00) [29]


> bems ©   (30.12.12 23:19) [70]


> в заголовке 'Expires' возвращается '24.02.2012'

Так вроде не по стандарту же. Должно быть типа
Expires: Tue, 31 Jan 2012 15:02:53 GMT
возмущение Indy резонно.


Pcrepair   (2012-12-30 23:22:00) [30]

может тогда уже использовать TMultiReadExclusiveWriteSynchronizer? чем писать свои реализации чтение-запись?
----------------------------
может ИНДИ(из комплекта ХЕ2) и огого, но вот спотыкалась при загрузке некоторых страниц, причем так спотыкалась что даже полное подавление исключений не помогло - программа(простейший код) просто зависала и все. а в РАД2010 и ХЕ3 тот же самый код работает нормально.
что касается кодовой страницы: в РАД2010 - кракозябры, в ХЕ3 - кырилица в норме( в 80%) случаев. так что выводы сделать можно и без огромного опыта


bems   (2012-12-30 23:19:00) [31]

поковырялся
по адресу ' http://www.electrosad.ru/Electronics/PP.htm' (или какому-то куда он редиректит) в заголовке 'Expires' возвращается '24.02.2012'
Инди ожидают увидеть в этой строкен в качестве разделителя даты либо '-' (и называют это поддержкой бажных серверов) либо пробел. Точку там они не ожидают, поэтому у них возникает эксцепшн. В качестве обработки они считают что указана дата 30.12.1899.
Наружу, как и ожидалось, этот эксцепшн не попадает


DVM   (2012-12-30 23:08:00) [32]


> Pcrepair ©   (30.12.12 22:55) [65]

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


DVM   (2012-12-30 23:07:00) [33]


> Pcrepair ©   (30.12.12 22:55) [65]
> DVM
>
> > Сделать потокобезопасное свойство не проблема если что.
>
>
> расскажи лучше об этом поподробнее.

TMyClass = class
private
 FCS: TCriticalSection;
 FMyProp: string;
 function GetMyProp: string;
 procedure SetMyProp(const AValue: string);
public
 property MyProp: string read GetMyProp write SetMyProp;
 ..

function TMyClass.GetMyProp: string;
begin
 FCS.Enter;
 try
   Result := FMyProp;
 finally
   FCS.Leave;
 end;
end;

function TMyClass.SetMyProp(const AValue: string);
begin
 FCS.Enter;
 try
   FMyProp := AValue;
 finally
   FCS.Leave;
 end;
end;


И ГЛАВНОЕ!!! Во всех методах самого TMyClass обращаться исключительно к MyProp, но не FMyProp!!!


DVM   (2012-12-30 22:57:00) [34]


> Pcrepair ©   (30.12.12 22:51) [64]


> в ХЕ2 ИНДИ был очень кривой, из за его косяков при загрузке
> страниц пробовал даже перейти на другие варианты(ICS и прочее)

Ну и что же не перешел? Ты извини, но твоя квалификация недостаточна, для объективной оценки качества работы Indy. Indy библиотека, до которой многим другим еще расти и расти.


> выбор кодировки даже почти работает (80% случаев).

какое отношение TIdHTTP имеет к каким либо кодировкам? Это вообще не его дело.


> 302 и 301 тоже работают, но видимо до ума еще доводить и
> доводить

Нет там никаких проблем, и не было, по крайней мере у меня.


bems   (2012-12-30 22:56:00) [35]


> что бы это значило?

что indy обрабатывает это исключение, значит не косяк (хотя могли бы и без эксцепшна проверить).
ну или ты его сам глушишь, проверь что это не так


Pcrepair   (2012-12-30 22:55:00) [36]

DVM

> Сделать потокобезопасное свойство не проблема если что.

расскажи лучше об этом поподробнее.
сейчас так:

type
 TLoader = class(TThread) (*создаем свой класс на базе ПОТОК*)
 private
    FUrl:string; (*поле класса типа СТРОКА для чтения -записи данных*)
    FIdHttp:TidHTTP;
    FSSL:TIdSSLIOHandlerSocketOpenSSL;
 protected
   procedure Execute; override;
   function LoadHtmlCode(const PageUrl: String):string; (*ФУНК загрузки страницы*)
   procedure ErrorLog(Url:string);
   function GetRandomUserAgent: string;
   function LoadPage(const Url:string):string;
   function DelJS(const Code:string):string;
   function RestoreFrame(const Code:string):string;
   function RestoreFullUrl(const Code:string; const RootUrl:string):string;
   function CorrectUrl(const UrlStr:string):string;
 public
   property Url: string read FUrl write FUrl;  (*свойство класа, доступное для всей программы*)
   constructor Create;
   destructor Destroy; override;
 end;

а раньше вообще

public
   var Url: string;  (*свойство класа, доступное для всей программы*)
   constructor Create;
   destructor Destroy; override;
 end;

и вроде все работало
твой вариант:


Pcrepair   (2012-12-30 22:51:00) [37]


> посмотри попадает ли этот эксцепшн в твой обработчик. если
> нет, то это внутренние дела indy

что бы это значило? это исключение видно только в режиме отладки, если запустить откомп. приложение то никаких сообщений не появляется,  все работает как предначертано

в ХЕ2 ИНДИ был очень кривой, из за его косяков при загрузке страниц пробовал даже перейти на другие варианты(ICS и прочее)
сейчас пробую ХЕ3 инди 10.5.9.0 - значительно лучше, выбор кодировки даже почти работает (80% случаев). 302 и 301 тоже работают, но видимо до ума еще доводить и доводить


DVM   (2012-12-30 22:46:00) [38]


> Pcrepair ©   (30.12.12 22:40) [60]


> вот еще что, это исключение есть даже при самом простом
> коде закачки. один к одному.

ну возникает исключение и что? не вижу повода для паники. У тебя этих исключений может возникнуть вагон и маленькая тележка, начиная от недоступности сайта, заканчивая некорректными HTTP заголовками и пр, на что инди вполне закономерно выбрасывает исключения. Если какие то типы исключений нельзя пропускать наружу, try...except тебе в помощь, желательно с логгированием.


DVM   (2012-12-30 22:46:00) [39]


> Pcrepair ©   (30.12.12 22:40) [60]


> вот еще что, это исключение есть даже при самом простом
> коде закачки. один к одному.

ну возникает исключение и что? не вижу повода для паники. У тебя этих исключений может возникнуть вагон и маленькая тележка, начиная от недоступности сайта, заканчивая некорректными HTTP заголовками и пр, на что инди вполне закономерно выбрасывает исключения. Если какие то типы исключений нельзя пропускать наружу, try...except тебе в помощь, желательно с логгированием.


bems   (2012-12-30 22:42:00) [40]

ну похоже что по этим адресам в хидерах где-то позвращается пустая дата.
посмотри попадает ли этот эксцепшн в твой обработчик. если нет, то это внутренние дела indy

в версии индейцев которая идет с дельфи XE2 я вижу что в случае эксцепшна в RawStrInternetToDateTime она просто возвращает false и считает жксцепшн обработанным. Правда в ней же в самом начала стоит проверка не передана ли в RawStrInternetToDateTime пустая строка, и в этом случае возвращается false без возникновения эксцепшна. но может оказаться что эту проверку добавили позже и в твоей версии ее еще нет.

в общем если индейцы сами обрабатывают это исключение то беспокоиться не нужно


Страницы:   1 2 3 4 5

Наверх

Титульная страница Поиск, карта сайта Написать письмо