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

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

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

Восстановить пароль
Повторная активизация e-mail

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

Ответ
 
Опции темы Поиск в этой теме
Старый 07.10.2014, 14:26   #1
vahdorf
Пользователь
 
Регистрация: 07.10.2014
Сообщений: 10
По умолчанию Проблема синхронизации потоков

Здравствуйте.

После нескольких лет пользования tidHttp появилось желание вынести загрузку (а точнее команду http.get(...)) в поток, а в перспективе вообще перейти на компонент Synapse. Написал фрагмент программы для работы с потоками. Бросил на форму кнопку Button1 и текстовое поле Memo1. Написал следующий код двумя модулями:
Код:
unit mainform;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses httpthread;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var httpthread: Thttpthread; i:integer; s:string;
begin
 for i:=1 to 1 do
  begin
  s:=inttostr(random(999));
  httpthread:=Thttpthread.Create(true);
  httpthread.URL:=s;
  httpthread.start;
  while WaitForSingleObject(httpthread.Handle, 50)=WAIT_TIMEOUT do Application.ProcessMessages;
  Memo1.lines.add('the end url='+httpthread.Result+'['+inttostr(httpthread.Handle)+']');
  httpthread.free;
  end;
end;

end.
Код:
unit httpthread;

interface

uses Classes;

type
  Thttpthread = class(TThread)
  private
   FUrl:string;
   FResult:string;
   procedure syncfunc;
  protected
    procedure Execute; override;
  public
   property URL:      string write FURL;
   property Result:   string read FResult;
  end;

implementation

uses SysUtils, Dialogs, mainform;

procedure Thttpthread.syncfunc;
begin
 //
end;


procedure Thttpthread.Execute;
begin

 Form1.Memo1.lines.add('execute url='+Furl);
 sleep(3000); // для теста, на самом деле здесь будет загрузка страниц, вида idhttp.get(...);
 Fresult:=Furl;
 Form1.Memo1.lines.add('finish  url='+Furl);

 //synchronize(syncfunc);
end;

end.
И почти сразу обнаружил проблему отслеживания завершения работы отдельного потока. Это хорошо видно по сообщениям в логе (memo1) и проявляется если несколько раз нажать на кнопку Button1 с паузой в 1-2 сек. Можно нажимать очень продолжительно, но строка в логе о завершении отдельного потока ("the end url=...") появится только после завершения всех! потоков, хотя строки "finished url=..." появляются сразу после выполнения Execute. handle потоков уникальны. Пробовал создавать потоки по нажатию нескольких разных кнопок со своими переменными типа Tthread, но и это не помогло. Нужна ваша помощь. Спасибо заранее!

Последний раз редактировалось vahdorf; 07.10.2014 в 15:28.
vahdorf вне форума Ответить с цитированием
Старый 07.10.2014, 14:57   #2
Человек_Борща
Старожил
 
Аватар для Человек_Борща
 
Регистрация: 30.12.2009
Сообщений: 11,426
По умолчанию

Что-то не понимаю что вы сделть-то хотите.

Создали доп. поток, в execute повесили его на 3 секунды, в ОСНОВНОМ потоке повесили основной поток с помощью WaitFor и продолжаете думать что все хорошо.

Увы нет

Опишите задачу.
Человек_Борща вне форума Ответить с цитированием
Старый 07.10.2014, 15:13   #3
Stilet
Белик Виталий :)
Старожил
 
Аватар для Stilet
 
Регистрация: 23.07.2007
Сообщений: 57,097
По умолчанию

Для начала:
Код:
unit httpthread;

interface

uses Classes;

type
  Thttpthread = class(TThread)
  private
   FUrl:string;
   FResult,s:string;
   procedure syncfunc;
  protected
    procedure Execute; override;
  public
   property URL:      string write FURL;
   property Result:   string read FResult;
  end;

implementation

uses SysUtils, Dialogs, mainform;

procedure Thttpthread.syncfunc;
begin
Form1.Memo1.lines.add(s);
end;


procedure Thttpthread.Execute;
begin

 s:='execute url='+Furl;synchronize(syncfunc);
 sleep(3000);
 Fresult:=Furl;
 s:='finish  url='+Furl;synchronize(syncfunc);
end;

end.
I'm learning to live...
Stilet вне форума Ответить с цитированием
Старый 07.10.2014, 15:19   #4
vahdorf
Пользователь
 
Регистрация: 07.10.2014
Сообщений: 10
По умолчанию

Цитата:
Сообщение от Человек_Борща Посмотреть сообщение
Опишите задачу.
Сейчас поставил перед собой такую задачу - вынести функцию загрузки страниц (команду idhttp.get(...)) в отдельный поток; и как только очередная страница загружена, обработать полученный текст; при этом может сразу поступить несколько команд на загрузку страниц.
(Метод onterminate для обработки результата не подходит, так как в дальнейшем этот поток-загрузчик будет использоваться для разного типа данных и вызываться из разных участков программы)

PS. sleep(3000) - это для теста, а так там будет загрузка текста страницы по переданному fURL (idhttp.get(fURL)), которая может занимать до нескольких секунд.

Последний раз редактировалось vahdorf; 07.10.2014 в 15:25.
vahdorf вне форума Ответить с цитированием
Старый 07.10.2014, 15:24   #5
vahdorf
Пользователь
 
Регистрация: 07.10.2014
Сообщений: 10
По умолчанию

Цитата:
Сообщение от Stilet Посмотреть сообщение
Для начала:
...
procedure Thttpthread.syncfunc;
begin
Form1.Memo1.lines.add(s);
end;

procedure Thttpthread.Execute;
begin
s:='execute url='+Furl;synchronize(syncfunc);
sleep(3000);
Fresult:=Furl;
s:='finish url='+Furl;synchronize(syncfunc);
end;
...
Про синхронизацию вывода текста в лог полностью согласен. Но ведение лога сейчас вообще не требуется, добавил эти строчки лишь для теста, чтобы увидеть что поток выполнил все операции, но в основную программу не дал сигнал завершения своей работы.
vahdorf вне форума Ответить с цитированием
Старый 07.10.2014, 17:13   #6
evg_m
Старожил
 
Регистрация: 20.04.2008
Сообщений: 5,526
По умолчанию

Цитата:
(Метод onterminate для обработки результата не подходит, так как в дальнейшем этот поток-загрузчик будет использоваться для разного типа данных и вызываться из разных участков программы)
Это не метод, это назначение ВНЕШНЕЙ (процедуры-метода) которая будет ВЫПОЛНЕНА при завершении потока.
И Вызываться она будет из одно и того же места (по завершении работы метода потока Excute).
и выполняться она будет ВНЕ потока(т.е. в основном потоке) и потому она не требует синхронизации.

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

Цитата:
но в основную программу не дал сигнал завершения своей работы.
Именно для этого и существует OnTerminate:=MyStopThread; (для получения сигнала о завершении потока)

Код:
procedure TFormX.MyStopThread(sender: TObject);
var
   s: string;
begin
  s:=(sender as THTTPThread).url;
 ShowMessage(s +'чтение закончено');
end;
программа — запись алгоритма на языке понятном транслятору

Последний раз редактировалось evg_m; 07.10.2014 в 17:19.
evg_m вне форума Ответить с цитированием
Старый 07.10.2014, 17:39   #7
vahdorf
Пользователь
 
Регистрация: 07.10.2014
Сообщений: 10
По умолчанию

Цитата:
Сообщение от evg_m Посмотреть сообщение
Именно для этого и существует OnTerminate:=MyStopThread; (для получения сигнала о завершении потока)

Код:
procedure TFormX.MyStopThread(sender: TObject);
var
   s: string;
begin
  s:=(sender as THTTPThread).url;
 ShowMessage(s +'чтение закончено');
end;
Пробовал в функцию MyStopThread добавлять флаг о том, что поток N завершил выполнение, но проверка этого флага (while not flagN do Application.ProcessMessages; ) в функции Button1click ничего не дает и вывод надписи "the end url=..." от нескольких потоков происходит одновременно, по завершению последнего запущенного потока.
Писать функции MyStopThread для каждого получаемого из интернета типа данных очень не хотелось бы...
vahdorf вне форума Ответить с цитированием
Старый 08.10.2014, 10:14   #8
evg_m
Старожил
 
Регистрация: 20.04.2008
Сообщений: 5,526
По умолчанию

Цитата:
Пробовал в функцию MyStopThread добавлять флаг о том, что поток N завершил выполнение, но проверка этого флага (while not flagN do Application.ProcessMessages; ) в функции Button1click ничего не дает и вывод надписи "the end url=..." от нескольких потоков происходит одновременно, по завершению последнего запущенного потока.
Выкинь из ButtonClick все ожидание
Цитата:
(while not flagN do Application.ProcessMessages; )
там или где-то еще должен быть только запуск.

поток САМ знает когда ему вызвать MyStopThread.
Ничего специально ждать не надо.

Но сможет он это сделать только во время "простоя" основного.
В твоем случае после окончания того самого цикла while not ... (точнее процедуры в которой он был заведен).
программа — запись алгоритма на языке понятном транслятору

Последний раз редактировалось evg_m; 08.10.2014 в 10:23.
evg_m вне форума Ответить с цитированием
Старый 08.10.2014, 11:29   #9
vahdorf
Пользователь
 
Регистрация: 07.10.2014
Сообщений: 10
По умолчанию

Мой вопрос сводится к следующему принципиальному моменту:
сейчас алгоритм работы основной функции такой - предварительное формирование данных для запроса, сам запрос в интернет (Idhttp.get) и обработка полученных данных.
Можно ли поместить Idhttp.get в поток, не разделяя единый алгоритм на две функции? Чтобы, как минимум, Idhttp.get не подвешивал сейчас программу (IdAntiFreeze не всегда помогает) а в последствие перейти на компонент от Synapse (там антифриза вообще нет никакого).

Последний раз редактировалось vahdorf; 08.10.2014 в 16:50.
vahdorf вне форума Ответить с цитированием
Старый 08.10.2014, 16:48   #10
vahdorf
Пользователь
 
Регистрация: 07.10.2014
Сообщений: 10
По умолчанию

Цитата:
Сообщение от Stilet Посмотреть сообщение
Для начала:
Код:
...
procedure Thttpthread.syncfunc;
begin
Form1.Memo1.lines.add(s);
end;

procedure Thttpthread.Execute;
begin
 s:='execute url='+Furl;synchronize(syncfunc);
 sleep(3000);
 s:='finish  url='+Furl;synchronize(syncfunc);
end;
...
Кстати еще вопрос по данной структуре построения потоков. Я так понял переменная text в данном случае будет глобальной? Не получится ли тогда рассинхрона при параллельном запуске нескольких потоков, когда один поток занес в переменную s='execute' а второй в этот момент изменяет ее на s='finish' и оба вызывают функцию syncfunc? Что добавится в этом случае в memo1?

Последний раз редактировалось vahdorf; 08.10.2014 в 16:50.
vahdorf вне форума Ответить с цитированием
Ответ


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



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Synchronize, Критические секции, другие варианты, что лучше для синхронизации потоков? bilibian Общие вопросы Delphi 15 04.03.2014 10:57
Проблема синхронизации WinAPI потоков Vadik(R) Общие вопросы Delphi 1 13.01.2014 01:27
AccessVolation при синхронизации потоков hiho Общие вопросы Delphi 2 27.10.2012 08:29
Проблема синхронизации потоков Teor Общие вопросы .NET 4 17.06.2011 15:50
Реализация функций синхронизации потоков. натка Помощь студентам 1 03.01.2008 15:26