Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 31.03.2011, 23:42   #1
spamer
Software Developer
Старожил
 
Аватар для spamer
 
Регистрация: 19.12.2008
Сообщений: 2,070
По умолчанию Работа и корректное завершение потока TThread в любой ситуации...

Всем привет. Возник вопрос по работе с потоками.
Вобщем, есть некий код ("запутанный в классы") который обрабатывает запрос к некоему сайту (авторизируется, забирает нужную информацию, отправляет информацию и т.п.). Как можно понять, при обработке всего этого программа ясное дело "молчит". Посему было решено, некоторый участок кода, который занимается обработкой - вынести в поток. Но появились же вопросы (хотя вроде и перечитал достаточно мануалов, но все равно на практике никогда с потоками не сталкивался поэтому решил уточнить/узнать некоторые моменты).
Вот мой пример того, что сделал (код подчищен - убраны лишние участки для читаемости):
Код:
  THttpThread = class(TThread)
    protected
      procedure Execute; override;
  end;
...

var
  HttpThread: THttpThread;

...

procedure TframeUserInfo.DoOnActionFrame(Sender: TObject);
begin
  try
    // Подготовка объектов и необходимых данных для них
    ...
    

    // Основной алгорит который хотелось бы вынести в поток

    // Нужен всего лишь один поток, поэтому выходим из обработчика если поток еще работает (данная часть кода отрабатывает не корректно, ибо поток по завершении не становится nil.  (з.ы. крит. секции тут использовать не хотелось бы). Как быть?
    if Assigned(HttpThread) then
      Exit;

    HttpThread := THttpThread.Create(True);
    try
      /// Что бы поток сам уничтожился после завершения - как я понимаю, этого не происходит, ибо код выше (проверка на выход) работает не правильно. Как быть?
      HttpThread.FreeOnTerminate := True;
      HttpThread.Resume;
      // Ждем пока поток полностью не отработает, после чего идем дальше. Смущает меня эта часть кода. Что скажите?
      while not HttpThread.Finished do
        Application.ProcessMessages;
    except
      // В случае чего кидаем мессагу, что бы корректно (вроде...) завершить и уничтожить поток. Правильно ли?
      on E: Exception do
        PostMessage(Handle, SS_FREE_HTTP_THREAD, 0, 0);
    end;

     // Работа с новыми/старыми объектами
     ...
  finally
    // Уничтожение всего того, что было создано
  end;
end;

...

procedure THttpThread.Execute;
begin
  // Некоторый код для обработки http запросов.
end;

...

// Обработчик мессаги для завершения и уничтожения потока в случае чего либо. Правильно ли?
procedure TframeUserInfo.SSFreeHttpThread(var Msg: TMessage);
begin
  if not Assigned(HttpThread) then
    Exit;

  HttpThread.Terminate;
  HttpThread.WaitFor;
  FreeAndNil(HttpThread);
end;
Так вот, из всего того, что написал выше, могли бы вы указать что в том коде не правильного и как исправить (писать за меня не прошу ). Ну и один из мучающих меня вопросов - а как быть, если пользователь запустил уже обработчик, поток работает, и тут бац пользователю захотелось закрыть программу? При закрытии программы может в таком случае бросать мессагу на завершение потока, как в примере выше? Или что бы вы могли посоветовать?
Заранее спасибо. Надеюсь никого не испугаю написанным здесь...
Будь проще и люди к тебе потянутся

Последний раз редактировалось spamer; 31.03.2011 в 23:44.
spamer вне форума Ответить с цитированием
Старый 01.04.2011, 01:04   #2
GunSmoker
Старожил
 
Регистрация: 13.08.2009
Сообщений: 2,581
По умолчанию

Кажется, тебе сюда.

По коду:

1. После этих строк:
Код:
      HttpThread.FreeOnTerminate := True;
      HttpThread.Resume;
Обращений к переменной HttpThread быть не должно. И вообще, FreeOnTerminate и отдельная переменная потока обычно несовместимы. Либо убирай FreeOnTerminate, либо меняй логику кода. FreeOnTerminate предназначен для "выстрелил - и забыл".

2.
Код:
  if not Assigned(HttpThread) then
    Exit;

  HttpThread.Terminate;
  HttpThread.WaitFor;
  FreeAndNil(HttpThread);
Заменяется:
Код:
  FreeAndNil(HttpThread);
Если внутри потока проверяется Terminated, то заменяется на:
Код:
  if not Assigned(HttpThread) then
    Exit;
  HttpThread.Terminate;
  FreeAndNil(HttpThread);
3. Не ясно, как поток согласован с DoOnActionFrame. В частности:
Код:
// Уничтожение всего того, что было создано
В том числе и объектов, необходимых для работы потока?
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
GunSmoker вне форума Ответить с цитированием
Старый 01.04.2011, 01:47   #3
spamer
Software Developer
Старожил
 
Аватар для spamer
 
Регистрация: 19.12.2008
Сообщений: 2,070
По умолчанию

Цитата:
И вообще, FreeOnTerminate и отдельная переменная потока обычно несовместимы.
Не понял, а за какую отдельную переменную потока вы говорите? Там же только один объект - HttpThread. Или я чего-то не так понял в этом предложении?
С остальными пунктами вроде более ли мение ясно...
Такс, а вот насчет этого:
Цитата:
3. Не ясно, как поток согласован с DoOnActionFrame.
...
В том числе и объектов, необходимых для работы потока?
DoOnActionFrame - это обработчик мессаги для одного из нескольких фреймов. Т.е. из некой формы отсылается мессага нужному фрейму, которая передает управление на некоторое время этому фрейму...вот.
// Уничтожение всего того, что было создано - тут уже идет непосредственное уничтожение всех темповых объектов которые должны были что-то передать главному, перед тем как главный объект запустит свою цепочку действий (можно сказать - основной алгоритм для соответствующего типа объекта); затем удаление именно основных объекта; некоторых менеджеров...ну и т.п.

Вобщем, насколько я понял, должно быть что-то типа такого?
Код:
procedure TframeUserInfo.DoOnActionFrame(Sender: TObject);
begin
  try
    // Подготовка объектов и необходимых данных для них
    ...
    if Assigned(HttpThread) then
      Exit;

    HttpThread := THttpThread.Create(True);
    try
      HttpThread.Resume;
      // Ждем пока поток полностью не отработает, после чего идем дальше. Смущает меня эта часть кода. Что скажите?
      while not HttpThread.Finished do
        Application.ProcessMessages;
    except
        PostMessage(Handle, SS_FREE_HTTP_THREAD, 0, 0);
    end;

     // Работа с новыми/старыми объектами
     ...
  finally
    // Уничтожение всего того, что было создано
  end;
end;
Код:
procedure TframeUserInfo.SSFreeHttpThread(var Msg: TMessage);
begin
  if not Assigned(HttpThread) then
    Exit;

  FreeAndNil(HttpThread);
end;
Кстати, внутри потока Terminated не проверяется. Там по большому счету вызывается всего лишь метод основного объекта, который выполняет бОльшую часть действий и который я захотел вынести в отдельный поток.
Ах и да, а как же быть с ситуацией закрытия приложения во время обработки потока? Не, ну можно конечно пойти более простым путем - выдавать диалог с информацией о состоянии обработки и этим же запретить закрытие приложения пока все не отработает...но все же интересно как это с потоками именно провернуть...

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

Последний раз редактировалось spamer; 01.04.2011 в 01:50.
spamer вне форума Ответить с цитированием
Старый 01.04.2011, 14:07   #4
Аватар
Старожил
 
Аватар для Аватар
 
Регистрация: 17.11.2010
Сообщений: 18,922
По умолчанию

Код:
HttpThread.Resume;
      // Ждем пока поток полностью не отработает, после чего идем дальше. Смущает меня эта часть кода. Что скажите?
      while not HttpThread.Finished do
        Application.ProcessMessages;
Не понятна сама идея - запускаем триаду и ждем пока не завершится. А зачем тогда вообще триада нужна?
Если бы архитекторы строили здания так, как программисты пишут программы, то первый залетевший дятел разрушил бы цивилизацию
Аватар вне форума Ответить с цитированием
Старый 01.04.2011, 16:41   #5
GunSmoker
Старожил
 
Регистрация: 13.08.2009
Сообщений: 2,581
По умолчанию

Цитата:
Сообщение от spamer Посмотреть сообщение
Не понял, а за какую отдельную переменную потока вы говорите? Там же только один объект - HttpThread.
Про него и говорю. FreeOnTerminte означает, что HttpThread может быть удалена в любой момент. Что означает, что обращение к ней после запуска потока не безопасно.

Это можно обойти, но чем обходить - лучше убрать FreeOnTerminate. Не для того оно предназначено.

Цитата:
Сообщение от spamer Посмотреть сообщение
DoOnActionFrame - это обработчик мессаги для одного из нескольких фреймов. Т.е. из некой формы отсылается мессага нужному фрейму, которая передает управление на некоторое время этому фрейму...вот.
// Уничтожение всего того, что было создано - тут уже идет непосредственное уничтожение всех темповых объектов которые должны были что-то передать главному, перед тем как главный объект запустит свою цепочку действий (можно сказать - основной алгоритм для соответствующего типа объекта); затем удаление именно основных объекта; некоторых менеджеров...ну и т.п.
"главному" - кому/чему?

Цитата:
Сообщение от spamer Посмотреть сообщение
Вобщем, насколько я понял, должно быть что-то типа такого?
Ты определись - ты хочешь действия в потоке выпонять синхронно или асинхронно?

Т.е. синхронно - это: что-то делаешь, запустил поток, потом ждёшь, пока поток не отработает, продолжаешь работать дальше.

Асинхронно - это: что-то делаешь, запустил поток, продолжаешь работать дальше, когда поток завершается - ты доделываешь ещё работу.

Продумай какие действия куда сувать. И где делать очистку. Так чтобы не получилось, что ты сперва данные грохнул, а потом с ними кто-то работать собирается.

Цитата:
Сообщение от spamer Посмотреть сообщение
Ах и да, а как же быть с ситуацией закрытия приложения во время обработки потока? Не, ну можно конечно пойти более простым путем - выдавать диалог с информацией о состоянии обработки и этим же запретить закрытие приложения пока все не отработает...но все же интересно как это с потоками именно провернуть...
В потоке нужно проверять условие выхода (Terminated) и досрочно прерывать работу.

Цитата:
Сообщение от Аватар Посмотреть сообщение
Не понятна сама идея - запускаем триаду и ждем пока не завершится. А зачем тогда вообще триада нужна?
Во время ожидания потока работает интерфейс и UI не виснет.
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
GunSmoker вне форума Ответить с цитированием
Старый 01.04.2011, 23:03   #6
spamer
Software Developer
Старожил
 
Аватар для spamer
 
Регистрация: 19.12.2008
Сообщений: 2,070
По умолчанию

Цитата:
"главному" - кому/чему?
Так сказать основной объект в котором есть некий алгоритм, который хотелось бы выполнять отдельно в потоке...

Цитата:
Ты определись - ты хочешь действия в потоке выпонять синхронно или асинхронно?
Вот...действия должны выполняться синхронно. Я так понимаю, выше приведенная часть кода (пост #3) как раз и является реализацией нечто похоже на синхронный тип потока. Так ведь или все же нет?
Будь проще и люди к тебе потянутся
spamer вне форума Ответить с цитированием
Старый 01.04.2011, 23:29   #7
eduard93
Форумчанин
 
Регистрация: 06.12.2010
Сообщений: 300
По умолчанию

Лучше заменить ProcessMessages на HandleMessage, чтобы не нагружать пустой работой проц.
eduard93 вне форума Ответить с цитированием
Старый 01.04.2011, 23:51   #8
GunSmoker
Старожил
 
Регистрация: 13.08.2009
Сообщений: 2,581
По умолчанию

Если надо синхронно, то получаем:

Код:
procedure TframeUserInfo.DoOnActionFrame(Sender: TObject);
begin
  try
    // Подготовка объектов и необходимых данных для них
    ...
    if Assigned(HttpThread) then
      Exit;

    HttpThread := THttpThread.Create(False);
    try
      while not HttpThread.Terminated do
        Application.HandleMessage;
    finally
      FreeAndNil(HttpThread);
    end; 

     // Работа с новыми/старыми объектами
     ...
  finally
    // Уничтожение всего того, что было создано
  end;
end;
Только надо не забыть при выходе из Execute потока дёрнуть главный поток (например, посылкой WM_NULL) - чтобы HandleMessage проснулся.

Ну и по указанной ранее ссылке загляни - там проще будет:

Код:
procedure TframeUserInfo.DoOnActionFrame(Sender: TObject);
begin
  try
    // Подготовка объектов и необходимых данных для них
    ...

    EnterWorkerThread;
    try
      // Сюда пиши код, который у тебя в Execute
    finally
      LeaveWorkerThread;
    end; 

     // Работа с новыми/старыми объектами
     ...
  finally
    // Уничтожение всего того, что было создано
  end;
end;
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
GunSmoker вне форума Ответить с цитированием
Старый 02.04.2011, 01:44   #9
spamer
Software Developer
Старожил
 
Аватар для spamer
 
Регистрация: 19.12.2008
Сообщений: 2,070
По умолчанию

А можно еще уточнить, останавливать поток будем же в Execute(), т.е. так? -
Код:
procedure THttpThread.Execute;
begin
    // выполняем некий необходимый код...
    Terminate; // Останавливаем поток.
    SendMessage(Application.Handle, WM_NULL, 0, 0); // Дергаем главный поток.
end;
Цитата:
В потоке нужно проверять условие выхода (Terminated) и досрочно прерывать работу.
Если так, то как же тогда будет выглядеть код, если мы останавливаем в Execute() поток? Так что ли? Но я так подозреваю, что явно что-то не оно.
Код:
procedure THttpThread.Execute;
begin
    while not Terminated do
    begin
    // выполняем некий необходимый код...
    Terminate; // Останавливаем поток.
    end;
    SendMessage(Application.Handle, WM_NULL, 0, 0); // Дергаем главный поток.
end;

...

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
   if Assigned(HttpThread) then
      HttpThread.Terminate;
end;
Цитата:
Ну и по указанной ранее ссылке загляни - там проще будет:
Да, там намного все проще, но хотелось бы уже с потоками заодно разобраться...
Кстати, а могли бы вы, если не составит труда, показать пример и асинхронного варианта потока?
Будь проще и люди к тебе потянутся

Последний раз редактировалось spamer; 02.04.2011 в 01:49.
spamer вне форума Ответить с цитированием
Старый 03.04.2011, 20:27   #10
GunSmoker
Старожил
 
Регистрация: 13.08.2009
Сообщений: 2,581
По умолчанию

Цитата:
Сообщение от spamer Посмотреть сообщение
А можно еще уточнить, останавливать поток будем же в Execute(), т.е. так?
Останов потока в Execute - это просто выход из Execute.

Terminate обычно вызывают по кнопке "Стоп".

Цитата:
Сообщение от spamer Посмотреть сообщение
Если так, то как же тогда будет выглядеть код, если мы останавливаем в Execute() поток? Так что ли? Но я так подозреваю, что явно что-то не оно.
А тебе разве в потоке нужны циклические действия? Вроде нет.

Должно быть:

Код:
procedure THttpThread.Execute;
begin
  // Делаем операцию 1
  if Terminated then
    Exit;
  // Делаем операцию 2
  if Terminated then
    Exit;
  // Делаем операцию 3
  if Terminated then
    Exit;
  // Делаем операцию 4
  if Terminated then
    Exit;
  // Делаем операцию 5
  if Terminated then
    Exit;
  // Готово
end;
Или:

Код:
procedure THttpThread.Execute;
begin
  while not Terminated do
  begin
    // Делаем очередную операцию
  end; 
end;
Ну и там ещё если какой код тебе надо - вставляем.

Цитата:
Сообщение от spamer Посмотреть сообщение
Кстати, а могли бы вы, если не составит труда, показать пример и асинхронного варианта потока?
Нет времени.
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
GunSmoker вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
C# BackgroundWorker - завершение работы потока Lastsummer Помощь студентам 2 16.03.2011 18:04
Работа с TThread t2skler Общие вопросы Delphi 20 16.02.2011 19:14
при закрытии потока(Tthread) посылать на форму код ответа или сообщение Человек_Борща Общие вопросы Delphi 2 14.12.2010 21:19
Работа с TThread pesi Общие вопросы Delphi 2 09.08.2010 14:12
Как отследить завершение выполнения потока TThread? Трик Общие вопросы Delphi 3 24.10.2007 14:55