![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Проблемы с потоками при закачке страниц
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 без возникновения эксцепшна. но может оказаться что эту проверку добавили позже и в твоей версии ее еще нет.
в общем если индейцы сами обрабатывают это исключение то беспокоиться не нужно