Форум: "Сети";
Текущий архив: 2004.09.12;
Скачать: [xml.tar.bz2];
ВнизОстановка 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;
Скачать: [xml.tar.bz2];
Память: 0.51 MB
Время: 0.043 c