Экспертная система Delphi.int.ru

Сообщество программистов
Общение, помощь, обмен опытом

Логин:
Пароль:
Регистрация | Забыли пароль?

Delphi.int.ru Expert

Другие разделы портала

Переход к вопросу:

#   

Статистика за сегодня:  


Лучшие эксперты

Подробнее »



Вопрос # 6 354

/ вопрос закрыт /

Здравствуйте!
Я делаю программу, некого "бота" для одного сайтика. В боте, конечно же, не обойтись без отдельного потока. И у меня проблема с этим самым потоком. Так вот, в программе нужно ввести капчу. (картинку с символами-защиту от ботов). Все это реализовал, все вроде бы работало, НО....
создаю поток так:
______
TThread:=TNEWThread.Create(True);
TThread.FreeOnTerminate:=True;
TThread.OnTerminate:=ThreadEndWork;
TThread.Resume;
______
еще есть процедура, которая выполняется после того как поток завершится
______
procedure TForm1.ThreadEndWork(Sender: TObject);
begin
Stop:=True;
Free_All; //вызов процедуры (http://savepic.ru/3862876.png)
Form1.Button1.Enabled:=True; //кнопка старт
Form1.Button2.Enabled:=False;//кнопка стоп
Form1.Memo1.Lines.Append('['+TimeToStr(Now)+'] Поток завершил работу.');
Form1.Memo1.Lines.Append('['+TimeToStr(Now)+'] Работа окончена.');
end;
end;
______
Например, в процедуре потока, в цикле (while not(Stop) do begin) идет создание IdHTTP (и еще некоторые компоненты, но не в этом суть), затем настраиваем IdHTTP (юзерагент и пр), затем гет запрос на сайт (обрабатываем в try except end и ловим ошибки, если они есть) и очистка созданных компонентов. (Вызов процедуры http://savepic.ru/3862876.png).
И так-же, примерно в центре кода этой процедуры есть маааленькая проверка на капчу. Так вот, если есть капча, поток берет картинку, выводит 2-ую форму с этой картинкой, с полем для ввода символов c картинки и кнопкой подтверждения ввода соответственно. Я не знал как иначе это сделать, поэтому я решил убивать поток при том условии если попадается капча. И получается, что поток берет картинку, выводит 2 форму, а себя завершает -- Exit;
Затем, когда капча введена - нажимем на кнопку, и она имитирует нажатие кнопки старт (Form1.Button1.Click;) т.е. создает тот самый поток.

В чем, собственно, проблема...
Посмотрите на рисунок. http://savepic.ru/3845466.png Думаю там общий смысл понятен. Так вот, работает поток, работает, все хорошо, ПОЯВЛЯЕТСЯ КАПЧА, выскакивает 2 форма, поток завершается, я ввожу капчу, нажимаю на баттон, создается тот самый поток. Затем программа работает, работает, И ВДРУГ, Х** ПОЙМИ ИЗ-ЗА ЧЕГО (скорее, из-за моей криворукости, но надеюсь, вы мне сумеете помочь) логирование перестает работать. Т.е. в лог информация что запрос отправлен и т.п. перестает заноситься. (в мемо). Посмотрел в сниффере -- запросы на сайт аналогично - не отправляются.
Я заинтересовался, что же случилось? Ведь информации в логе об окончании работы не было (за нее отвечает процедура ThreadEndWork). Я кинул на форму баттон, на него нацепил код:
if (TThread.Terminated) then Showmessage('поток завершен')
else showmessage('поток не завершен');

И что я вижу? По логу видно что поток не работает, но и Terminated потока равен False, то есть, ПОТОК ЖИВ(!) и, может, "завис".
причем завис поток в сааамом низу программы, где, по идее, цикл должен был вернуть в начало, к созданию Idhttp, но этого, видимо, не произошло. Хотя переменная Stop до сих пор равна False. Программу уже запущена 2 часа, и полтора часа поток уже не работает, хоть он и
"жив", о чем свидетельствует его собственное свойство Terminated.
Извините за много буков, и за грамотность в том числе, как говорится, "накипело", уже 5-й день не могу понять в чем проблема, ведь вся занимаемая потоком память очищается, прямого обращения к компонентам форм тоже нет, да и логирование веду исключительно используя PostMessage. Надеюсь, что я подробно описал проблему....
Уважаемые эксперты, что это может быть?

Овчинников Алексей Иванович Вопрос закрыт (ответы не принимаются, мини-форум закрыт)

Вопрос задал: Овчинников Алексей Иванович (статус: Посетитель)
Вопрос отправлен: 10 января 2013, 01:00
Состояние вопроса: закрыт, ответов: 1.

Ответ #1. Отвечает эксперт: Вадим К

Здравствуйте, Овчинников Алексей Иванович!
Вы решаете задачу изначально неверно. Итак, упростим постановку задачи. Потоку нужно в какой то момент времени остановиться, дать пользователю сделать ввод и потом, получив результат, обработать его и пойти дальше. Решается эта задача так.
Когда поток решает, что время настало, он посылает с помощью Sendmessage главному потоку сообщение. Пока главный поток обрабатывает сообщение, сам робочий поток будет приостановлен. Поэтому главный поток может читать/модифицировать поля рабочего потока без синхронизации.
Одна с фишек sendmessage - она может возвратить результат в виде целого числа. Хотя если у нас есть возможность модифицировать поля рабочего потока - никаких проблем. После SendMessage рабочий поток проверяет свои поля и решает что делать. Если нужно подождать - просто вызывает свой метод suspend, который заставляет поток заснуть. Главный поток на этот момент уже отобразил на форме все что нужно.
Когда пользователь принял решение, главный поток модифицирует поля рабочего потока (это опять безопасно, так как поток спит) и вызывает метод resume рабочего потока, который заставляет его проснуться и продолжить работу.


Есть ещё метод с использованием модификатора Synchronyze, но он менее гибок.

Цитата:

Terminated потока равен False, то есть, ПОТОК ЖИВ(!) и, может, "завис".

Далеко не факт.

Но есть ещё одна проблема в коде.
TThread.FreeOnTerminate:=True;
 TThread.OnTerminate:=ThreadEndWork;
Если выставить указанное свойсто, то код ThreadEndWork будет исполнен в контексте рабочего потока, а не главного. А обращаться к элементам формы не из главного потока - плохо. Возможны самые разные "глюки".

Ответ отправил: Вадим К (статус: Академик)
Время отправки: 10 января 2013, 18:14


Мини-форум вопроса

Всего сообщений: 6; последнее сообщение — 24 января 2013, 15:31; участников в обсуждении: 3.
DNK

DNK (статус: Студент), 10 января 2013, 22:05 [#1]:

Цитата (Вадим К):

Если выставить указанное свойсто, то код ThreadEndWork будет исполнен в контексте рабочего потока, а не главного.
Данный код, если верить сорсам, засинхронизирован.
"Digital Networked Knight"
Вадим К

Вадим К (статус: Академик), 11 января 2013, 11:50 [#2]:

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

На начальном этапе лучше не полагаться, что оно заработает, а просто не вызывать методов, которые работают с gui напрямую. Позже, когда придет понимание, будет легче.
Галочка "подтверждения прочтения" - вселенское зло.
DNK

DNK (статус: Студент), 11 января 2013, 12:43 [#3]:

Про странности речи не было, я только заметил в каком именно потоке выполняется код. Если конечно никто не додумался CheckSynchronize вызвать из-под другой нитки.
"Digital Networked Knight"
Овчинников Алексей Иванович

Овчинников Алексей Иванович (статус: Посетитель), 11 января 2013, 19:47 [#4]:

Проблема решена. Ошибка заключалась в том что не было прописано свойство ReadTimeout в HTTP и поток тупо "залипал\зависал" при плохом коннекте.

Тем не менее, у меня появился другой вопрос.
Перед завершении потока ( Exit; ) нужно очищать занимаемую ним память? Я о FreeAndNil(idHTTP); Или в результате завершения работы потока занимаемая память очистится сама если при создании параметр потока FreeOnTerminate был указан на True?
Вадим К

Вадим К (статус: Академик), 14 января 2013, 13:11 [#5]:

Все, что Вы создали, обязаны уничтожить. Либо назначить ответственного за это. Для компонентов, которые устанавливают на форму, она становиться ответственной за них. Но если сами создали и ответственного не назначили - нужно удалять самостоятельно. Это Вам на java.
Галочка "подтверждения прочтения" - вселенское зло.
Овчинников Алексей Иванович

Овчинников Алексей Иванович (статус: Посетитель), 24 января 2013, 15:31 [#6]:

Спасибо!

26 февраля 2013, 11:46: Вопрос закрыт (решение принял DNK): Пожалуйста!

Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.

Версия движка: 2.6+ (26.01.2011)
Текущее время: 22 февраля 2025, 11:27
Выполнено за 0.02 сек.