Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.09.12;
Скачать: CL | DM;

Вниз

Остановка TIdTCPServer   Найти похожие ветки 

 
начинающий   (2004-07-04 19:04) [0]

Начал изучать компоненты Indy. И вот какая беда приключилась: чтобы остановить сервер TIdTCPServer делаю следующее:
procedure TForm1.BitBtn2Click(Sender: TObject);
begin
 if IdTCPServer1.Active then
 begin
  IdTCPServer1.Active := FALSE;
  IdTCPServer1.Bindings.Clear;
  Memo1.Lines.Add("[Stopped]")
 end
end;

Но самое интересное дальше: так работает лишь тогда, когда в OnDisconnect ничего не прописано. Стоит лишь мне поместить туда хоть небольшой участок кода, самый безвредный (типа Memo1.Lines.Add), как действие вышеуказанного обработчика приводят к зависанию программы на несколько секунд, после чего выводится информация об исключении, а после чего - собственно и происходит OnDisconnect.
В чём мой прокол? Спасибо!


 
alienserg   (2004-07-06 05:09) [1]

все правильно. самый верный способ остановить сервер это сделать
IdTCPServer1.Active := FALSE;

Твои проблемы в том, что обработчик события OnDisconnect привязан к треду коннекта и выполняется в нем, а не в основном VCL потоке. Поэтому всю VCL работу надо синхронизировать.
Через Synchronize() или с помощью TIdSync или посылая сообщеня окну с помощью PostMessage.
В OnExecute это тоже надо всегда иметь в виду. Более того, не только работа по отрисовке на форме должна синхронизироваться, но и работа с базой.


 
Digitman ©   (2004-07-06 14:20) [2]


> alienserg   (06.07.04 05:09) [1]


> но и работа с базой


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


 
alienserg   (2004-07-06 17:56) [3]

Digitman ©   (06.07.04 14:20) [2]
ну, положим, это ты загнул лишнего

Лучше пересинхронировать чем недосинхронизировать :)
Да, в общем случае это не всегда верно. Всякий раз надо выяснять, являются ли используемые компоненты доступа к базе thread safe. В данный момент работаю с MySQL через ZEOS, и надо самому заботиться о синхронизации. ZEOS не является полностью thread safe. Приходится поддерживать пул экземпляров датамодуля и динамически связывать их с тредами в моменты работы с базой. Это и есть в моем понимании синхронизация. Недопущение одновременной работы с экземпляром компонента из разных тредов.
Кстати, ты не подскажешь, является ли Direct Oracle Access(DOA) tread safe?

начинающий   (04.07.04 19:04)  
Еще один момент, когда надо заботиться о синхронизации:
Клиент1 запросил у сервера например структуру директории.
Сервер шлет ему в ответ 100 строк через WriteLn.
В это же самое время клиент2 шлет клиенту1 сообщение через сервер.
Сервер ищет в списке активних тредов тред клиента1 и использует его TIdPeerThread.Connection для отправки сообщения. Но отправка сообщения клиенту1 от клиента2 всегда выполняется в треде клиента2! Поэтому надо синхронизировать. Иначе строки смешаются или вообще заклинит.
Та же самая ситуация, если ты пытаешься что-то отправить по нажатию кнопки на форме. Или по таймеру опросив базу и найдя записи, которые надо отослать клиенту.
Все синхронизировать.


 
Digitman ©   (2004-07-06 18:05) [4]


> alienserg   (06.07.04 17:56) [3]


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


ну это и в большинстве других клиентских частях так же выглядит, ничего удивительного нет здесь

но ничто не мешает использовать отдельный набор экземпляров кл.компонентов для доступа к серв.части

в том же БДЕ, который так же не thread-safe, достаточно в отдельном потоке создать отдельную связку TSession+TDatabase+TDataSet - и можно смело обращаться к базе, невзирая на другие потоки, делающие то же самое


> является ли Direct Oracle Access(DOA) tread safe


понятия не имею


 
Digitman ©   (2004-07-06 18:14) [5]


> alienserg   (06.07.04 17:56) [3]


> Все синхронизировать


да, но далеко не всегда обязательно это требуется делать с использованием очереди сообщений осн.потока

синхронизация с осн.потоком требуется, в 1-ю очередь тогда, когда доп.поток обращается к визуализированным VCL-объектам


 
alienserg   (2004-07-06 19:43) [6]

Digitman ©   (06.07.04 18:14) [5]
синхронизация с осн.потоком требуется
Возможно я не совсем корректно выразился... Мы говорим об одном и том же.
Под синхронизацией я имел ввиду не привязку к VCL-потоку и не столько использование Synchronize(), сколько разруливание конкурентного доступа к объектам из разных тредов. Временный захват ресурса в монопольное пользование или создание_своих/использование_сушествующих очередей сообщений и их обработчиков или создание пула ресурсов и динамическое связывание их с тредами, когда тем нужно.


 
Digitman ©   (2004-07-07 08:11) [7]


> alienserg   (06.07.04 19:43) [6]


ок.. с эти согласен


 
начинающий   (2004-07-07 15:05) [8]

Господа, я чувствую, что в следующем сообщении уже будет пример на ассемблере! Не хочу показаться неблагодарным, но я никак не пойму как конкретно нужно синхронизировать?
Может onDisconnect должен содержать нечто наподобие: AThread.Synchronize(someProcedure). Или использовать критические секции при деактивации сервера? И как использовать упомянутый выше TIdSync(если нужно, конечно)? Он же абстрактный. Может, кто подобрее и посвободнее приведет маленький пример, или даст ссылку на ресурс, который разъяснит мою темноту (хотя и говорят, что во многой мудрости много печали, но и глупцу жить не весело).


 
alienserg   (2004-07-07 19:11) [9]

начинающий   (07.07.04 15:05) [8]

К господам отношения не имеем, мы вольные труженики.

По Indy основной кладезь знаний это ньюсгруппы в atozedsoftware.indy
например newsgroup:atozedsoftware.indy.servers.tcp
их там куча и все полезные. Отвечают сами разработчики, причем очень быстро и всегда.

Вот тебе примеров пара, не на ассемблере.

**** Использование TIdSync ****
type
 TStartFileReceive = class(TIdSync)
 protected
 public
   FSize: integer;
   FSenderName: string;
   procedure DoSynchronize; override;
   constructor Create(AThread: TIdThread; aSize: integer; senderName:string);
   class procedure Add(AThread: TIdThread; aSize: integer; senderName:string);
 end;

implementation

{ TStartFileReceive }

class procedure TStartFileReceive.Add(AThread: TIdThread; aSize: integer; senderName:string);
begin
 with TStartFileReceive.Create(AThread, aSize, senderName) do begin
   try
     Synchronize;
   finally
     Free;
   end;
 end;
end;

constructor TStartFileReceive.Create(AThread:TIdThread; aSize:integer; senderName:string);
begin
 inherited Create(AThread);
 FSize:=aSize;
 FsenderName:=senderName;
end;

procedure TStartFileReceive.DoSynchronize;
var
 fileForm: TfileReceiveForm;
 msgForm: TamcForm;
begin
 msgForm:=mainForm.getBuddyForm(FsenderName,false);
 if msgForm<>nil then begin
   fileForm:=msgForm.fileReceiveForm;
   if fileForm<>nil then begin
     fileForm.DrawBar(4,inttostr(FSize));
     fileForm.receiveState:=sReceiving;
     fileForm.DrawBar(1,"receiving...");
     fileForm.btDir.Enabled:=false;
     fileForm.ProgressBar1.Max:=FSize;
   end;
 end;
end;


чтобы все это дело сработало, я в OnExecute делаю
TStartFileReceive.Add(AThread, aFilesize, senderName);

в твоем случае это будет что-то типа
 TDebugLog = class(TIdSync)
 protected
 public
   FLine: string;
   procedure DoSynchronize; override;
   constructor Create(AThread: TIdThread; aLine: string);
   class procedure Add(AThread: TIdThread; aLine: string);
 end;


**** Использование PostMessage ****
type
 TFileParams = record
   fileName: string;
   fileSize: integer;
   md5hash_server: string;
   md5hash_local: string;
 end;
 PFileParams = ^TFileParams;
 
 TsfSendForm = class(TForm)
   ...
 private
   ...
   procedure DoFileSendStart(recAddr: integer); message WM_SEND_START;
   ...
 public
   ...
 end;

const
 WM_SEND_START       = WM_USER + 1;

implementation

procedure TsfSendForm.DoFileSendStart(recAddr: integer);
var
 fileParams: PFileParams;
 fileItem: TListItem;
 i: integer;
begin
 fileParams:=PFileParams(recAddr);
 lbFile.Caption:=fileParams.fileName;

 fileItem:=TListItem.Create(lvFiles.Items);
 for i:=0 to 3 do fileItem.SubItems.Add("");
 fileItem.SubItems[0]:=inttostr(fileParams.fileSize);
 fileItem.SubItems[1]:=fileParams.md5hash_local;
 fileItem:=lvFiles.Items.AddItem(fileItem);
 fileItem.Caption:=fileParams.fileName;

 ProgressBar1.Max:=fileParams.fileSize;
 ProgressBar1.Position:=0;
 DrawBar(1,"sending...");
 DrawBar(2,"0%");
 DrawBar(3,inttostr(0));
 DrawBar(4,inttostr(fileParams.fileSize));
 Dispose(fileParams);
end;


вызов DoFileSendStart (для этого надо иметь под рукой хэндл окна, которому посылаешь сообщение):

var
 fileParams: PFileParams;
...
 New(fileParams);
 fileParams.fileName:=Copy(FFileName,Length(FLocalDir)+2,999999);
 fileParams.fileSize:=FFileSize;
 fileParams.md5hash_local:=Fmd5hash;
 PostMessage(TsfSendForm(FParentForm).Handle,WM_SEND_START,integer(fileParams),0);


В общем Welcome to Threaded World!


 
alienserg   (2004-07-07 19:27) [10]

еще надо добавить
uses IdSync;


 
Ozone ©   (2004-07-08 12:39) [11]

alienserg   (07.07.04 19:11) [9]

Решил заюзать работу IdSync по вашему примеру.


чтобы все это дело сработало, я в OnExecute делаю
TStartFileReceive.Add(AThread, aFilesize, senderName);

1. А надо ли его выгружать? Если да, то где?

procedure TStartFileReceive.DoSynchronize;
var
fileForm: TfileReceiveForm;
msgForm: TamcForm;
begin
msgForm:=mainForm.getBuddyForm(FsenderName,false);
...


2. Зачем так делать? Нельзя ли просто на прямую обращаться к форме?

3. Что лучше IdSync или PostMessage?


 
alienserg   (2004-07-08 13:11) [12]

Ozone ©   (08.07.04 12:39) [11]
1. А надо ли его выгружать? Если да, то где?
Не надо.
Add это классовый метод, который создает экpемпляр потомка TIdSync, вызывает его Synchronize и потом делает Free.
  try
    Synchronize;
  finally
    Free;
  end;


2. Зачем так делать? Нельзя ли просто на прямую обращаться к форме?
Конечно можно напрямую.
Я просто выдрал кусок из моего приложения. В моем случае я создаю формы динамически, как ICQ создает переговорные формы. Поэтому я и выбираю нужную мне форму таким образом:
msgForm:=mainForm.getBuddyForm(FsenderName,false);

3. Что лучше IdSync или PostMessage?
Я применяю и то и другое. IdSync сделали специально для синхронизации отрисовки на форме из треда TIdTcpServer и его потомков.
PostMessage более гибкий. Я его использую например когда создаю отдельный поток с TIdTcpClient чтобы отрисовывать прогресс передачи файла.
Если используешь PostMessage, заботься чтобы WM_USER + YOUR_CUSTOM_MESSAGE_OFFSET не пересеклось ненароком с каким-нибудь другим подобным идентификатором в пользовательской зоне. Например я использую ThemeEngine и они также используют WM_USER + MESSAGE_OFFSET Предварительно порылся в их коде и убедился, что значения не пересекаются.


 
Ozone ©   (2004-07-08 13:21) [13]

alienserg   (08.07.04 13:11) [12]

Т.е. если я буду юзать WM_USER + 1, WM_USER + 2 и т.д., то я могу кому-то "навредить"?


 
Ozone ©   (2004-07-08 13:24) [14]

И еще, когда та так делаешь

var
fileParams: PFileParams;
...
New(fileParams);
...
PostMessage(TsfSendForm(FParentForm).Handle,WM_SEND_START,integer(fileParams),0)


То в какой момент можно будет выгрузить fileParams. Ведь если это сделать сразу, то в приемнике мы ничего не получим (не успеем)?


 
Ozone ©   (2004-07-09 05:30) [15]

UP


 
Digitman ©   (2004-07-09 08:10) [16]


> Ozone ©   (09.07.04 05:30) [15]


на стороне приемника в этом случае :

var
 fileparams: PFileParams;
..
 fileparams := PFileParams(Message.wParam); //прием параметра
... //обработка параметра
 Dispose(fileparams); //освобождение памяти


 
Ozone ©   (2004-07-09 08:39) [17]

Digitman ©   (09.07.04 08:10) [16]

Спасибо.



Страницы: 1 вся ветка

Текущий архив: 2004.09.12;
Скачать: CL | DM;

Наверх




Память: 0.53 MB
Время: 0.036 c
14-1093147071
Baron
2004-08-22 07:57
2004.09.12
Здесь должен быть САБЖ


3-1092745901
Len
2004-08-17 16:31
2004.09.12
База для хранения "разных" файлов


1-1093216809
массив
2004-08-23 03:20
2004.09.12
Какой самый корркетный способ добавления шрифта в систему ?


1-1093589718
anarhi
2004-08-27 10:55
2004.09.12
Label


1-1093862827
TEXHAPb
2004-08-30 14:47
2004.09.12
Как запретить редактировать текст ячейки в TStringGrid