![]() |
| ||
|
|
Новости |
Поиск |
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)
В рабочем режиме, за счет подавления исключений, все это конечно не вылазит, но:
- закачиваются не все страницы(хотя все УРЛ валидные и функцией закачки закачиваются без вопросов)
- после закрытия Арр выводится сообщение о многочисленных маленьких утечках памяти
По теории потоки не должны пересекаться. каждый имеет свою память. вывод результата через КС. такое впечетление что потоки как то мешают друг другу
Внимание!!! Вопрос: в чем может быть причина появления исключений и утечки памяти именно в режиме многопотоковой закачки?
bems (2012-12-27 21:45:00) [81]
constructor TLoader.Create(FIdHttp := TIdHTTP.Create(nil));
что это?
в теле конструктора присваивай, там же где FreeOnTerminate := True
Pcrepair (2012-12-27 21:31:00) [82]вот это: (Url, FIdHttp)
но не будем отвлекаться на пустяки, итого нужно следующее
type
TLoader = class(TThread)
private
FUrl:string;
FIdHttp:TidHTTP;
protected
procedure Execute; override;
function TLoader.LoadHtmlCode(const Url: String): String;
public
property Url: string read FUrl write FUrl; (*свойство класа, доступное для всей программы*)
constructor Create;
destructor Destroy; override;
end;
implementation
uses uPageLoader, uLoadPage, uGlobalVar;
constructor TLoader.Create(FIdHttp := TIdHTTP.Create(nil));
begin
inherited Create(True); (*Поток создаем в состоянии «Приостановлен»*)
FreeOnTerminate := True; (* Поток освободит ресурсы при окончании работы*)
Self.Priority := tpNormal;
end;
destructor TLoader.Destroy;
begin
FreeAndNil(FIdHttp);
end;
function TLoader.LoadHtmlCode(const Url: String): String;
begin
Result:=LoadPage(Url, FIdHttp)
end;
procedure TLoader.Execute;
var Code:string;
begin
Code:=LoadHtmlCode(Url, IdHttp);
CS.Enter; (*вход в КС*)
gStore.Add(Code); (*передаем результат в ГП*)
Cs.Leave; (*выход из КС*)
end;
end.
+++++++++++++++другой модуль++++++++++++++++++++
function LoadPage(const Url: String; IdHttp: TIdHttp): String;
begin
Result := AnsiLowerCase(IdHttp.Get(Url))
end;
а вызов потока останется прежний?
bems (2012-12-27 20:30:00) [83]объявить. и в конструкторе присвоить FIdHttp := TIdHTTP.Create(nil);
в деструкторе соответственно освободить
что именно не встречал?
Pcrepair (2012-12-27 20:07:00) [84]что то такую форму записи Result:=LoadPage(Url, FIdHttp) нигде в букварях не встречал
FIdHttp - это в
TLoader = class(TThread)
private
FIdHttp:TidHTTP;
объявить?
bems (2012-12-27 18:56:00) [85]типа
function LoadPage(const Url: String; IdHttp: TIdHttp): String;, где FIdHttp - поле TLoader, присваиваемое в конструкторе
begin
Result := AnsiLowerCase(IdHttp.Get(Url))
end;
function TLoader.LoadHtmlCode(const Url: String): String;
begin
Result:=LoadPage(Url, FIdHttp)
end;
Pcrepair (2012-12-27 17:26:00) [86]
> bems © (27.12.12 15:04) [13]
> вызывать функции других модулей можно, но нельзя при этом
> обращаться к общим данным без синхронизации.
> например можно сделать функции LoadPage еще один параметр
> типа TidHttp и передавать в него экземпляр, с которым работает
> только вызывающий поток
это типа
function TForm1.LoadPage(const S:string; idHttp: TidHttp):string;
begin
idHttpLoad := TIdHTTP.Create(nil);
try
LoadPage := (AnsiLowerCase(idHttpLoad.Get(S)));
finally
idHttpLoad.Free;
end;
end;
Сергей М. (2012-12-27 15:44:00) [87]
> а не вызывать функции из доп. модулей?
Вопрос о выносе частей кода в отдельные юниты - это не более чем вопрос об удобстве восприятия текста проекта, ну и еще вопрос использования общих фрагментов кода со стороны других модулей.
bems (2012-12-27 15:04:00) [88]вызывать функции других модулей можно, но нельзя при этом обращаться к общим данным без синхронизации.
например можно сделать функции LoadPage еще один параметр типа TidHttp и передавать в него экземпляр, с которым работает только вызывающий поток
Pcrepair (2012-12-27 14:59:00) [89]
> А зачем она вообще нужна именно в описании класса ? Кто
> к ней обращается кроме членов самого класса TLoader ?
это рудимент предыдущих экспериментов
получается нужно писать код нескольких потоков, имеющих каждый свой idHTTP.Get и весь код обработки страницы, а не вызывать функции из доп. модулей? чтоб гарантированно не было исключений?
может тогда уже свой класс правильней написать? с использованием внутри этого класса потоков.
создавать потомок класса, передавать ему значение(УРЛ) + написать класс НАКОПИТЕЛЬ в который передавать асинхронно результат работы
с ООП знаком пока только теоритически
Сергей М. (2012-12-27 14:42:00) [90]
> перем Html прописана как свойство класса, хотя можно прописать
> ее ка переменную в ПАБЛИК. в чем разница? и как правильно
А зачем она вообще нужна именно в описании класса ? Кто к ней обращается кроме членов самого класса TLoader ?
Судя по приведенному коду это вообще переменная временного назначения, место которой - в стеке, т.е. среди локальных переменных подпрограмм.
RWolf (2012-12-27 14:31:00) [91]
> что нужно написать одинаковый код для скажем 10 потоков
> и для каждого прописать свой idHTTP, типаidHTTP_1idHTTP_2
> и так далее и для каждого свое создание-уничтожение и все
> прочее?
ну, а как думаешь, данные глобального объекта idHTTP каким-то волшебным образом избегут разрушения, когда их модифицируют несколько потоков?
для каждого свой, конечно.
Сергей М. (2012-12-27 14:16:00) [92]
> к описанным проблемам это пока не имеет отношения
"пока" может и не имеет, тебе виднее.
А потом поимеет - попросту забудешь про синхронизацию, как сейчас "забыл" про нее в CallStore.
Лучше таки сразу предусмотреть синхронизацию для всех потенциально возможных случаев. И обратить при этом внимание на TMultiReadExclusiveWriteSynchronizer как возможно более разумную альтернативу TCriticalSection.
Pcrepair (2012-12-27 14:03:00) [93]anonims
> использование одного Loader, SSL для ВСЕХ потоков.
что ты имеешь ввиду? что нужно написать одинаковый код для скажем 10 потоков и для каждого прописать свой idHTTP, типа
idHTTP_1
idHTTP_2 и так далее и для каждого свое создание-уничтожение и все прочее?
++++++++++++++++++++++++++++++
Сергей М.
> Разумеется "читающий" поток, тот который "выгребает" все
> это хозяйство из gStore, точно так же обязан входить в КС
> при этом.
все правильно конечно, но в текущей ситуации(стенд) обращение к накопителю идет вручную только после окончания работы всех потоков(контроль по диспетчеру задач), так что к описанным проблемам это пока не имеет отношения. вместе с тем, кто знает программу более функциональную чем Диспетчер задач, для наблюдения за потоками выбранного приложения?
и еще вопрос: там перем Html прописана как свойство класса, хотя можно прописать ее ка переменную в ПАБЛИК. в чем разница? и как правильно
+++++++++++++++++++++++++++++
Игорь Шевченко
там выводит код из модулей ИНДИ
типа
а вот что это значит и что с этим делать?
unit IdSSLOpenSSL
procedure TIdSSLIOHandlerSocketOpenSSL.ConnectClient;
var
LPassThrough: Boolean;
begin
(*RLebeau 1/11/07:
In case a proxy is being used, pass through any data from the base class unencrypted when setting up that connection.
///В случае, если используется прокси-сервер, пройти через любые данные из базового класса, незашифрованные при создании связи
We should do this anyway since SSL hasn't been initialized yet!
///Мы должны сделать это в любом случае, поскольку SSL не была инициализирована
*)
LPassThrough := fPassThrough;
fPassThrough := True;
try
inherited ConnectClient; (*исключение*)
finally
fPassThrough := LPassThrough;
end;
DoBeforeConnect(Self);
// CreateSSLContext(sslmClient);
// CreateSSLContext(SSLOptions.fMode);
StartSSL;
end;
Только в многопоточном режиме исключение?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Socket Error 10056
модуль IdStack
сообщение:
procedure TIdStack.RaiseSocketError(AErr: integer);
begin
(*
RRRRR EEEEEE AAAA DDDDD MM MM EEEEEE !! !! !!
RR RR EE AA AA DD DD MMMM MMMM EE !! !! !!
RRRRR EEEE AAAAAA DD DD MM MMM MM EEEE !! !! !!
RR RR EE AA AA DD DD MM MM EE
RR RR EEEEEE AA AA DDDDD MM MM EEEEEE .. .. ..
Please read the note in the next comment.
*)
if AErr = Id_WSAENOTSOCK then begin
// You can add this to your exception ignore list for easier debugging.
// However please note that sometimes it is a true error. Your program
// will still run correctly, but the debugger will not stop on it if you
// list it in the ignore list. But for most times its fine to put it in
// the ignore list, it only affects your debugging.
raise EIdNotASocket.CreateError(AErr, WSTranslateSocketErrorMsg(AErr));
end;
(*
It is normal to receive a 10038 exception (10038, NOT others!) here when
*shutting down* (NOT at other times!) servers (NOT clients!).
If you receive a 10038 exception here please see the FAQ at:
http://www.IndyProject.org/
If you insist upon requesting help via our email boxes on the 10038 error
that is already answered in the FAQ and you are simply too slothful to
search for your answer and ask your question in the public forums you may be
publicly flogged, tarred and feathered and your name may be added to every
chain letter / EMail in existence today."
Otherwise, if you DID read the FAQ and have further questions, please feel
free to ask using one of the methods (Carefullly note that these methods do
not list email) listed on the Tech Support link at:
http://www.IndyProject.org/
RRRRR EEEEEE AAAA DDDDD MM MM EEEEEE !! !! !!
RR RR EE AA AA DD DD MMMM MMMM EE !! !! !!
RRRRR EEEE AAAAAA DD DD MM MMM MM EEEE !! !! !!
RR RR EE AA AA DD DD MM MM EE
RR RR EEEEEE AA AA DDDDD MM MM EEEEEE .. .. ..
*)
raise EIdSocketError.CreateError(AErr, WSTranslateSocketErrorMsg(AErr));
end;
anonims (2012-12-27 12:52:00) [94]В одном потоке все работает => LoadPage не потокобезоасна. (использует и меняет не локальные данные).
function TLoader.LoadHtmlCode(const Url:string):string;
begin
Result:=LoadPage(Url); (*вызываем функцию загрузки страницы из uPageLoader*)
end;
unit uPageLoader;
interface
uses Classes, SysUtils, idHTTP, StrUtils, IdSSL, IdSSLOpenSSL;
function LoadPage(const Url:string):string; (*Основная функция Юнита*)
var
Loader:TidHTTP;
SSL:TIdSSLIOHandlerSocketOpenSSL;
использование одного Loader, SSL для ВСЕХ потоков.
> тут много чего, но все работает, смотри в начало
тоже все глобальные объекты едины для всех потоков.
Сергей М. (2012-12-27 12:34:00) [95]Разумеется "читающий" поток, тот который "выгребает" все это хозяйство из gStore, точно так же обязан входить в КС при этом.
Сергей М. (2012-12-27 12:32:00) [96]
> вывод результата через КС
procedure TLoader.CallStore;
begin
cs.Enter;
try
gStore.Add(Html); // вот теперь ресурс действительно защищен, никакие утечки теперь не грозят
finally
cs.Leave;
end;
end;
icWasya (2012-12-27 11:55:00) [97]Ну а в однопоточном режиме всё работает?
Ну то есть вот так
Procedure Loader(Url:string);
var ThreadLdr: TLoader;
begin
ThreadLdr:=Tloader.Create(True);
ThreadLdr.Priority:=tpNormal;
ThreadLdr.FreeOnTerminate:=True;
ThreadLdr.Url:=Url;
(*ThreadLdr.Start; вместо этого*)
ThreadLdr.Execute;//<<==-- вот это
ThreadLdr.Free;//<<==--
end;
Игорь Шевченко (2012-12-27 11:51:00) [98]
> Вопрос: в чем может быть причина появления исключений и
> утечки памяти именно в режиме многопотоковой закачки?
Наверное в режиме отладки и строки, на которых проиходит исключение, показываются.
Не томи.
Сергей М. (2012-12-27 11:51:00) [99]В упор не видно защиты глоб.ресурса gStore критической секцией.
Ты объект Cs вообще зачем создавал, не для этого ли как раз ?
brother (2012-12-27 11:45:00) [100]> исключений и утечки памяти
исключения дают утечки...