![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Удаление TADOConnection в потоке [D7, MSSQL]
Артем © (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(), она проверит флажок, если он сброшен, то просто оставит экзкмпляр коннекта в покое, а если установлен, то закроет соединение и разрушит экземпляр коннекта.
В обоих исходах поток, в конце-концов, самоубъётся апстену.
Не надо никаких разных потоков для подключения и уничтожения коннектов.