Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Удаление TADOConnection в потоке [D7, MSSQL]


Страницы:   1 2 3

Артем ©   (2011-08-02 21:40:00) 

Здравствуйте мастера!
Проблема в том что нужно удалять соединения в потоке.

Вот примерный код потока:


type TMyThread= class(TThread);
....
FObjList: TObjectList;
Crit: TCriticalSection;
....
end;

constructor TMyThread.Create(CreateSuspended: Boolean);
begin
inherited Create(CreateSuspended);
FObjList:= TObjectList.Create(True);
Crit:= TCriticalSection.Create;
end;

procedure TMyThread.AddObject(Obj: TADOConnection);
begin
Crit.Enter;
FObjList.Add(Obj);
Crit.Leave;
end;

procedure TMyThread.Execute;
begin
while not Terminated do
 begin
  sleep(10); //чтоб поток не грузил процессор
  if FObjList.Count>0 then
   FObjList.Delete(0);
 end;
end;

//потом в основном потоке
...
var
T:TMyThread;
...
//поток создается и т.д.

...
//вызывается метод AddObject у потока
T.AddObject(Connection);


Прошелся дебагером по потоку объект вроде как уничтожается, но в ProcessExplorer'е увеливается счетик User Handles ровно на количество созданных мной соединений. CoInitialize и CoUninitialize никак не помогают.
Соединения надо удалять так, потому что сединение с СУБД MsSQL может не пройти по каким-либо причинам, и соединение подвисает, а при уничтожении соединения и основной поток, поэтому уничтожение убрано в отдельный поток.

Помогите кто-нибудь, кто сталкивался с подобным.


sniknik   (2011-08-04 11:17:00) [1]

по msdn вызовы должны быть строгопарные. освобождение ресурсов...
т.е. раз вызвал CoInitialize то неважно, что тебе нужно, должен вызвать и CoUninitialize.


OW   (2011-08-04 09:40:00) [2]

скажите, а
procedure CoUninitialize;               external ole32 name 'CoUninitialize';
обязательно вызывать, если поток где я вызываю CoInitializeEx(nil, 0); мне больше не нужен?


sniknik   (2011-08-04 00:02:00) [3]

> с разницей.
блин.
- не ходите дети через дорогу на красный может сбить машиной!
- а вроде тут ходят троллейбусы...
- да без разницы чем собьет. убить может.
- с разницей.
типа машина на бензине, а троллейбусы на электричестве... и это ОЧЕНЬ важно, просто чтобы возразить. хотя говорили о другом.

> CoInitializeEx должна быть вызвана в самом потоке, который работает с OLE, COM, ADO, etc
а я о чем? с самого начала. ты читай, что пишут прежде чем возражать... повторенными словами оппонента.


Игорь Шевченко   (2011-08-03 22:59:00) [4]

sniknik ©   (03.08.11 20:45) [37]


> без разницы


CoInitialize calls CoInitializeEx and specifies the concurrency model as single-thread apartment.

с разницей.


> т.е. любом случае не обслуживает несколько потоков одной
> инициализацией


CoInitializeEx должна быть вызвана в самом потоке, который работает с OLE, COM, ADO, etc


sniknik   (2011-08-03 21:11:00) [5]

Игорь Шевченко ©   (03.08.11 18:14) [35]
можно чуток изменить тест, чтобы уж "по любому". на AV нарваться тут "дело техники", а вот так гарантированный глюк должен быть -
unit ConnThread;

interface

uses
 Classes, ADODB, SysUtils, Windows, ActiveX;

type
 TConnThread = class(TThread)
 private
   Connection: TADOConnection;
 protected
   procedure Execute; override;
 public
   destructor Destroy;  override;
   constructor Create(CreateSuspended: Boolean);
 end;

implementation

constructor TConnThread.Create(CreateSuspended: Boolean);
begin
 inherited;

 //CoInitialize(nil);
 CoInitializeEx(nil, COINIT_MULTITHREADED);
 Connection:= TADOConnection.Create(nil);

 FreeOnTerminate:= true;
 Resume;
end;

destructor TConnThread.Destroy;
begin
 FreeAndNil(Connection);
 CoUninitialize;

 inherited;
end;

procedure TConnThread.Execute;
begin
 {CoInitialize(nil);
 try
   Connection:= TADOConnection.Create(nil);}

   //Connection.ConnectionString:= 'Provider=SQLOLEDB.1;Integrated Security=SSPI'; //mssql ëîêàëüíî
   Connection.ConnectionString:= 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\;Extended Properties=Text'; //Jet
   Connection.Connected:= true;

 {  FreeAndNil(Connection);
   Sleep(0);
 finally
   CoUninitialize;
 end;}
end;

end.

что делать понятно... 2 варианта.


sniknik   (2011-08-03 20:45:00) [6]

> мне кажется, должно быть CoInitializeEx (...COINIT_MULTITHREADED)
без разницы
http://msdn.microsoft.com/en-us/library/ms695279%28v=vs.85%29.aspx
...
The concurrency model and initialization options for the thread. Values for this parameter are taken from the COINIT enumeration. Any combination of values from COINIT can be used, except that the COINIT_APARTMENTTHREADED and COINIT_MULTITHREADED flags cannot both be set. The default is COINIT_MULTITHREADED.
...
Because OLE technologies are not thread-safe, the OleInitialize function calls CoInitializeEx with the COINIT_APARTMENTTHREADED flag. As a result, an apartment that is initialized for multithreaded object concurrency cannot use the features enabled by OleInitialize.
...
т.е. любом случае не обслуживает несколько потоков одной инициализацией, фактически VCL от мелкософта... ;)

> У меня и раньше память ... бла.бла.бла.
чем дальше, тем больше ощущение, что я тебе "про бабушку", а ты все "про дедушку" (уж куда более очевидно, проверил, да/нет ситуация такая, нет опять фигню свою несет)... и кому это надо? парься сам.


Артем   (2011-08-03 18:22:00) [7]


> работает? а теперь закоментарь внутри Execute и раскоментарь
> в деструкторе/конструкторе... работает? или AV? (что возможно
> при "пересечениях" потоков. ну или у тебя очень быстрый
> комп... успевает, добавь нагрузку в Execute).

У меня и раньше память высвобождалась занятая коннектом, количество хендлов оставалось, прежним. При создании увеливались, а при уничтожении не удалялись.


> мне кажется, должно быть CoInitializeEx (...COINIT_MULTITHREADED)

Спасибо, надо почитать мануалы.


Игорь Шевченко   (2011-08-03 18:14:00) [8]


> у тебя опять CoInitialize делается не в том потоке в котором
> используется


мне кажется, должно быть CoInitializeEx (...COINIT_MULTITHREADED)


sniknik   (2011-08-03 16:05:00) [9]

> TConnThread = class(T Thread)
???
а как же декларируемая независимость от основного потока?


sniknik   (2011-08-03 16:02:00) [10]

вот для примера
unit ConnThread;

interface

uses
Classes, ADODB, SysUtils, Windows, ActiveX;

type
 TConnThread = class(TThread)
 private
   Connection: TADOConnection;
 protected
   procedure Execute; override;
 public
   destructor Destroy;  override;
   constructor Create(CreateSuspended: Boolean);
 end;

implementation

constructor TConnThread.Create(CreateSuspended: Boolean);
begin
 inherited;

 //CoInitialize(nil);
 //Connection:= TADOConnection.Create(nil);

 FreeOnTerminate:= true;
 Resume;
end;

destructor TConnThread.Destroy;
begin
 //FreeAndNil(Connection);
 //CoUninitialize;

 inherited;
end;

procedure TConnThread.Execute;
begin
 CoInitialize(nil);
 try
   Connection:= TADOConnection.Create(nil);
   FreeAndNil(Connection);
 finally
   CoUninitialize;
 end;
end;

end.


и выполнить, до абсурда  

uses ConnThread;

procedure TForm1.Button1Click(Sender: TObject);
var
 i: integer;
begin
 for i:= 1 to 1000 do
   TConnThread.Create(true);
end;


работает?
а теперь закоментарь внутри Execute и раскоментарь в деструкторе/конструкторе... работает? или AV? (что возможно при "пересечениях" потоков. ну или у тебя очень быстрый комп... успевает, добавь нагрузку в Execute).


sniknik   (2011-08-03 15:57:00) [11]

> Если потока, то для этого FreeOnTerminate в True установлено.
при чем здесь FreeOnTerminate,  у тебя опять CoInitialize делается не в том потоке в котором используется...


Артем   (2011-08-03 15:09:00) [12]

Поток создается в основном потоке, тут я только код самого потока подключения привел как советовал DiamondShark.


> создание потока и деструктор делаются в основном потоке
> вообще то... т.что в принципе ты ничего не изменил.

Деструктор чего, потока или коннекта. Если потока, то для этого FreeOnTerminate в True установлено. Деструктор будет вызван когда закончиться выполнение процедуры Execute.


sniknik   (2011-08-03 14:18:00) [13]

> а где Free?
а, нашел.


sniknik   (2011-08-03 14:16:00) [14]

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

+
создание потока и деструктор делаются в основном потоке вообще то... т.что в принципе ты ничего не изменил.

++
а где Free?


Артем   (2011-08-03 14:08:00) [15]

unit ConnThread;

interface

uses
 Classes, ADODB, SysUtils, InterfacedThread, Windows, ActiveX;

type
 TConnThread = class(TInterfacedThread)
 public
   Connection:TADOConnection;
   Connected:boolean;
   ConnectionString:string;
   FM:boolean; //из основного потока по
               //этой переменной узнает что подключение есть
   destructor Destroy;  override;
   constructor Create(T:Boolean);
 private
   Flag:Boolean;
   { Private declarations }
 protected
   procedure Execute; override;
 published
 end;

implementation

constructor TConnЕhread.Create(T: Boolean);
begin
 inherited;
 Flag:=False;
 CoInitialize(nil);
 Connection:=TADOConnection.Create(nil);
end;

destructor TConnThread.Destroy;
begin
 if not Flag then
   if Assigned(Connection) then
     FreeAndNil(Connection);
 CoUninitialize;
 inherited;
end;

procedure TConnThread.Execute;
begin
 try
   FM:=False;
   if Connected then
     Connection.Connected:=True
   else
     Connection.Connected:=False;
   FM:=True;
 except
 end;
end;

end.


Все равно не удаляются хендлы. FreeOnTerminate стоит True.


sniknik   (2011-08-03 13:00:00) [16]

> Но вот почему в процедуре Execute не так все хорошо удаляется.
другой поток, не инициализирована COM модель, обьект завязан на "чужую"... что хорошего может быть?

> Спасибо, попробую.
а нормально, на асинхронной/событийной логике слабо?


Артем   (2011-08-03 12:50:00) [17]


> Неправильно.
>
> Сделай функцию, которая принимает строку подключения, а
> вернуть должна экземпляр коннекта.
> Пусть функция создаёт экземпляр коннекта, настраивает параметры
> подключения и передаёт экземпляр в поток для открытия соединения.
>
> Потом функция пусть отсчитывает таймаут, и если поток не
> завершился, то устанавливает потоку флажок "коннект уже
> не нужен, удавись", и возвращяет NIL.
>
> А поток пусть ждёт подключения хоть до морковкина заговенья.
>  Когда процедура потока получит возврат управления из Connection.
> Open(), она проверит флажок, если он сброшен, то просто
> оставит экзкмпляр коннекта в покое, а если установлен, то
> закроет соединение и разрушит экземпляр коннекта.
> В обоих исходах поток, в конце-концов, самоубъётся апстену.
>
>
> Не надо никаких разных потоков для подключения и уничтожения
> коннектов.

Спасибо, попробую.


Артем   (2011-08-03 12:48:00) [18]


> А что такое, по-твоему, User Handles?
> Ну, вот, например, подсистема СОМ создаёт невидимые окна
> для обслуживания маршалинга. Окно -- это один из типов User
> Handle. Оно тебя сильно волнует?

Если честно, то я даже и не знаю что именно подразумевается под User Handles в Process Explorer.
А так да, волнуют. Моя программа должна работать по несколько недель подряд. Представь себе сколько их накопится за это время, после этого наверное ОС сдохнет.

Для эксперимента вызывал в AddObject сразу FreeAndNil(Obj). Коннект нормально удалился, хендлы тоже удалились. Также пробовал делать FObjList.Add(Obj), а потом FObjList.Delete(0), тоже все нормально удалилось. Но вот почему в процедуре Execute не так все хорошо удаляется.


sniknik   (2011-08-03 12:43:00) [19]

> Зачем тебе код этой функции?
ни этой, ни другой не нужно. нужно то что покажет реализацию полностью, хоть вообще без фунций

> У меня был вопрос почему после уничтожения коннекта остаются User Handles.
тебе ответили. нет закрытия без ожидания завершения операции, а если уничтожать "насильно" терминейтом то там вообще все будет не завершено, не освобождено.
+ таймаут, на "предвидение" очередного конекта в ту же сторону. стандартное поведение ADO.

> При чём здесь пятая дельфя?
в пятой не работало.
> Он говорит про случай физической недоступности компьютеров.
ну вот стоит у меня 30 сек, доступен, не доступен сервер мне не важно, через 30 отвалится - "кончилось время".  

> Нету, хоть ты тресни.
а зачем его прерывать? этого не нужно, сокеты всегда в своих потоках, имхо, все что ему нужно это дать ответ "ожидающему концу" через 30 сек.
и опять имхо, именно так оно и работает. (не встречался с другим поведением... исключая 5ю дельфю)


DiamondShark   (2011-08-03 12:43:00) [20]


> Артем   (03.08.11 10:02) [18]

Неправильно.

Сделай функцию, которая принимает строку подключения, а вернуть должна экземпляр коннекта.
Пусть функция создаёт экземпляр коннекта, настраивает параметры подключения и передаёт экземпляр в поток для открытия соединения.
Потом функция пусть отсчитывает таймаут, и если поток не завершился, то устанавливает потоку флажок "коннект уже не нужен, удавись", и возвращяет NIL.

А поток пусть ждёт подключения хоть до морковкина заговенья. Когда процедура потока получит возврат управления из Connection.Open(), она проверит флажок, если он сброшен, то просто оставит экзкмпляр коннекта в покое, а если установлен, то закроет соединение и разрушит экземпляр коннекта.
В обоих исходах поток, в конце-концов, самоубъётся апстену.

Не надо никаких разных потоков для подключения и уничтожения коннектов.


Страницы:   1 2 3

Наверх

Титульная страница Поиск, карта сайта Написать письмо