-
Здравствуйте мастера! Проблема в том что нужно удалять соединения в потоке. Вот примерный код потока:
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;
...
...
T.AddObject(Connection);
Прошелся дебагером по потоку объект вроде как уничтожается, но в ProcessExplorer'е увеливается счетик User Handles ровно на количество созданных мной соединений. CoInitialize и CoUninitialize никак не помогают. Соединения надо удалять так, потому что сединение с СУБД MsSQL может не пройти по каким-либо причинам, и соединение подвисает, а при уничтожении соединения и основной поток, поэтому уничтожение убрано в отдельный поток. Помогите кто-нибудь, кто сталкивался с подобным.
-
ADO соединение это вообще то COM объект... и он может не уничтожаться, а ждать какое то время следующего, если они быстро друг за другом идут, это ускоряет процесс.
но у тебя и без "тонкостей" ошибок хватает... используешь поток как список объектов "из вне" потока... странно. ссылка на объект в 2 местах минимум... объект (vcl) не "убивается", а просто удаляется из списка... еще более странно. а говоришь уничтожается... где? наверное в "..." или в "...", хотя нет в "...", или не так, а так "все переписать нафиг" все, что стесняешься показать это один большой глюк. + нет надобности в потоках, есть асинхронный режим, там он сам потоками "рулит".
> Помогите кто-нибудь, кто сталкивался с подобным. шутишь? подобного не повторить... где уж столкнуться.
-
> объект (vcl) не "убивается", а просто удаляется из списка.
У него же TObjectList, а не TList Хотя в целом - безусловно говнокод
-
> Хотя в целом - безусловно говнокод так это, продемонстрируете мастер-код? а ту поржут... а?
-
-
TObjectLIst сам освобождает память при удалении объекта из списка.
-
> Артем (02.08.11 21:40) > > Соединения надо удалять так, потому что сединение с СУБД > MsSQL может не пройти по каким-либо причинам, и соединение > подвисает, а при уничтожении соединения и основной поток, > поэтому уничтожение убрано в отдельный поток.
Реализация работы с потоком сделана неверно. Критическая секция ничего не защищает, да и сам поток не нужен ибо -> "Кто к нам с мечом придет, тот от меча и погибнет" (С) к/ф "Александр Невский" -расшифрую, тот поток, в котором был создан TADOConnection будет "висеть" в любом случае, если зависнет TADOConnection, в каком бы ты потоке его не закрывал(освобождал).
-
> > Артем (02.08.11 21:40) > > > > Соединения надо удалять так, потому что сединение с СУБД > > MsSQL может не пройти по каким-либо причинам, и соединение > > подвисает, а при уничтожении соединения и основной поток, > > > поэтому уничтожение убрано в отдельный поток. > > Реализация работы с потоком сделана неверно. Критическая > секция ничего не защищает, да и сам поток не нужен ибо - > > > "Кто к нам с мечом придет, тот от меча и погибнет" (С) к/ф > "Александр Невский" -расшифрую, тот поток, в котором был > создан TADOConnection будет "висеть" в любом случае, если > зависнет TADOConnection, в каком бы ты потоке его не закрывал(освобождал). >
Поток в котором создается TADOConnection не подвиснет (сделано так чтоб не подвисал)
-
> Артем (03.08.11 07:34) [7]
Кем сделано? Почитай про COM объекты , апартаменты....
-
Мной сделано. Главное что поток не подвиснет при подключении/отключении. Но подвиснет при уничтожении соединения. Как его лучше уничтожить, чтобы не завис поток?
-
> У него же TObjectList, а не TList ;( не обратил внимания.
> так это, продемонстрируете мастер-код? а ту поржут... а? писать просто так? абстрактный код? ... даже попытка достойна ржача. - "когда у общества нет цветовой дифференциации штанов, то нет цели! А когда нет цели…" © Кин-Дза-Дза в смысле, код должен иметь цель, а код без цели лишен смысла...
> Поток в котором создается TADOConnection не подвиснет (сделано так чтоб не подвисал) > CoInitialize и CoUninitialize никак не помогают. они рассчитаны на поток, и обязательны для каждого потока, т.е. сначала инициализируется COM модель, после в нем создается COM объект. а у тебя, "твоя модель" совсем не работает, т.к. "завязки" у обьекта на другой поток.
p.s. если ты уверен, что все правильно, и все отвергаешь, то тебе не сюда, тебе в микрософт писать нужно... вот, что за фигня, написали операционку не совместимую с твоей программой...
-
> p.s. если ты уверен, что все правильно, и все отвергаешь, > то тебе не сюда, тебе в микрософт писать нужно... вот, > что за фигня, написали операционку не совместимую с твоей > программой...
Я сюда и написал, потому что не уверен что правильно сделал. В общем, мне надо чтобы так было: Основной поток, например по таймеру, соединяется с БД, выполняет запросы и т.д. (при соединении поток не зависнет, даже если нет физического соединения с сервером, но зависнет при уничтожении). Как уничтожить соединение чтобы основной поток не завис (или если вынести в отдельный поток работу с БД чтобы этот поток не зависал)?
-
> Артем (03.08.11 08:02) [9]
> Мной сделано
В таком варианте не поможет.
> Как его лучше уничтожить, чтобы не завис поток?
Если речь про основной поток, то например создавать, работать и уничтожать соединение и/или другие ADO компоненты в одном дополнительном потоке. Кстати тогда возможно все ограничится TADOCommand для доп. потока. Пусть висит доп. поток. Отвиснет, возникнет ошибка - передашь ее в основной поток. И еще в этом случае придется решать проблемы корректной передачи результатов запроса из одного потока в другой. Или смириться тем, что при ошибках в соединении возможно подвисание пользовательского интерфейса, но работать в основном потоке и не иметь других проблем. Тут тебе видней.
-
Работа с БД не связана с пользовательским интерфейсом, она вообще должна идти фоново. А подвисание потока, в котором выполняются запросы, недопустимо. Например, если нет соединения с СУБД, но есть с самим компьютером то подвиснет ненадолго (на время таймаута, заданного в adoconnection'е), или если нет связи с компьютером, то зависнут может надолго (у меня было и по 2-3 минуты). Для работы программы даже подвисания на 10 сек. недопустимы.
Вот я и хочу, чтобы в основном потоке выполнялось соединение, выполнялись запросы, а сам adoconnection уничтожался бы в другом, чтобы основной не подвисал в случае отсутствия связи.
-
> Как уничтожить соединение чтобы основной поток не завис (или если вынести в отдельный поток работу с БД чтобы этот поток не зависал)? сделать все независимым от основного, "от а до я", и в этом случае совершенно неважно, что какой то из потоков "завис". > она вообще должна идти фоново. о!!! > А подвисание потока, в котором выполняются запросы, недопустимо. с чего это если используется синхронный режим (совет про асинхронный ты проигнорировал) и есть вполне уместный таймаут (это не зависание) от обязательного завершения операции (дать ответ).
> уничтожался бы в другом, чтобы основной не подвисал в случае отсутствия связи. "висит" (тут это неправильное слово) не уничтожение, а ожидание выполняемой операции, пасть даже результатом таймаут, а операция у тебя выполняется где?...
-
> пасть пусть
кстати а что вообще за бредовая идея уничтожать коннект в процессе работы? ели он постоянно нужен. а если не нужен... ну пусть себе "повисит" дополнительный поток, не связанный с основным... чем он мешает то?
-
> кстати а что вообще за бредовая идея уничтожать коннект > в процессе работы? ели он постоянно нужен. а если не нужен. > .. ну пусть себе "повисит" дополнительный поток, не связанный > с основным... чем он мешает то?
Если соединение не проходит (коннект завис или еще что-то), то его надо уничтожить. А ждать пока пройдут все таймуаты нельзя. CommandTimeOut и ConnectionTimeout не срабатывают если нет физического соединения с компьютером, на котором работает MSSQL Server (просто к сети не подключен или вообще выключен), в остальных они срабатывают.
-
> Артем (03.08.2011 09:45:16) [16]
> А ждать пока пройдут все таймуаты нельзя. Тогда откуда ты решил, что соединение не проходит?
-
Есть функция, которая делает соединение с помощью дополнительного потока. В функцию передаются ссылка на коннект и таймаут. Функция создает поток, передает в поток коннект. Потом в цикле проверяет значение флажка в потоке, и если в течение времени таймаута не флажек False, то поток убивается, функция возвращает False. Или True если флажек стал True до истечения таймаута.
Если функция возвращает false, то он передается в другой поток на уничтожение, чтобы основной поток не задерживать своими таймаутами. В этом другом потоке коннект и должен уничтожаться.
-
> CommandTimeOut и ConnectionTimeout не срабатывают ???? у тебя, что пятая дельфя? только там, да и то без обновлений, была проблема с таймаутами. хотя, решаемая, правда через генофонд.
> В этом другом потоке коннект и должен уничтожаться. он там, или оттуда у тебя и уничтожается, но таймаут от операции все одно останется на операции, и с этим ничего в синхронном режиме не поделать.
> Есть функция .... бла.бла.бла. меньше слов больше дел.. кода!
-
> > CommandTimeOut и ConnectionTimeout не срабатывают > ???? у тебя, что пятая дельфя? только там, да и то без обновлений, > была проблема с таймаутами. хотя, решаемая, правда через > генофонд.
Пробовал в седьмой дельфи и XE, все равно не срабатывает таймаут.
> > В этом другом потоке коннект и должен уничтожаться. > он там, или оттуда у тебя и уничтожается, но таймаут от > операции все одно останется на операции, и с этим ничего > в синхронном режиме не поделать.
Если я отправил коннект на уничтожение, то мне уже без разницы что он подвис.
> > Есть функция .... бла.бла.бла. > меньше слов больше дел.. кода!
Зачем тебе код этой функции? У меня был вопрос почему после уничтожения коннекта остаются User Handles.
-
> sniknik © (03.08.11 11:56) [19] > > CommandTimeOut и ConnectionTimeout не срабатывают???? > у тебя, что пятая дельфя?
При чём здесь пятая дельфя? Он говорит про случай физической недоступности компьютеров. Здесь ни при чём не только дельфя, но и ни АДО ни МССКЛ дбпровайдер, ни дблиб. Это фича сокетов. Ну нету никакой возможности прервать сокетный connect(), ждущий ответа от недоступного хоста. Нету, хоть ты тресни.
Инфа 100%, что при недоступности хоста ConnectionTimeout курит в углу, а управление возвращается, когда TCP-стек соблаговолит. Так себя ведёт и АДО с МССКЛ дбпровайдером, и ОДБЦ драйвер, дотНЕТ-клиент. А всё, сцуко, потому, что все они, в конце-концов, вызывают синхронный сокетный connect().
-
> У меня был вопрос почему после уничтожения коннекта остаются > User Handles.
А что такое, по-твоему, User Handles? Ну, вот, например, подсистема СОМ создаёт невидимые окна для обслуживания маршалинга. Окно -- это один из типов User Handle. Оно тебя сильно волнует?
-
> Артем (03.08.11 10:02) [18]
Неправильно.
Сделай функцию, которая принимает строку подключения, а вернуть должна экземпляр коннекта. Пусть функция создаёт экземпляр коннекта, настраивает параметры подключения и передаёт экземпляр в поток для открытия соединения. Потом функция пусть отсчитывает таймаут, и если поток не завершился, то устанавливает потоку флажок "коннект уже не нужен, удавись", и возвращяет NIL.
А поток пусть ждёт подключения хоть до морковкина заговенья. Когда процедура потока получит возврат управления из Connection.Open(), она проверит флажок, если он сброшен, то просто оставит экзкмпляр коннекта в покое, а если установлен, то закроет соединение и разрушит экземпляр коннекта. В обоих исходах поток, в конце-концов, самоубъётся апстену.
Не надо никаких разных потоков для подключения и уничтожения коннектов.
-
> Зачем тебе код этой функции? ни этой, ни другой не нужно. нужно то что покажет реализацию полностью, хоть вообще без фунций
> У меня был вопрос почему после уничтожения коннекта остаются User Handles. тебе ответили. нет закрытия без ожидания завершения операции, а если уничтожать "насильно" терминейтом то там вообще все будет не завершено, не освобождено. + таймаут, на "предвидение" очередного конекта в ту же сторону. стандартное поведение ADO.
> При чём здесь пятая дельфя? в пятой не работало. > Он говорит про случай физической недоступности компьютеров. ну вот стоит у меня 30 сек, доступен, не доступен сервер мне не важно, через 30 отвалится - "кончилось время".
> Нету, хоть ты тресни. а зачем его прерывать? этого не нужно, сокеты всегда в своих потоках, имхо, все что ему нужно это дать ответ "ожидающему концу" через 30 сек. и опять имхо, именно так оно и работает. (не встречался с другим поведением... исключая 5ю дельфю)
-
> А что такое, по-твоему, User Handles? > Ну, вот, например, подсистема СОМ создаёт невидимые окна > для обслуживания маршалинга. Окно -- это один из типов User > Handle. Оно тебя сильно волнует?
Если честно, то я даже и не знаю что именно подразумевается под User Handles в Process Explorer. А так да, волнуют. Моя программа должна работать по несколько недель подряд. Представь себе сколько их накопится за это время, после этого наверное ОС сдохнет.
Для эксперимента вызывал в AddObject сразу FreeAndNil(Obj). Коннект нормально удалился, хендлы тоже удалились. Также пробовал делать FObjList.Add(Obj), а потом FObjList.Delete(0), тоже все нормально удалилось. Но вот почему в процедуре Execute не так все хорошо удаляется.
-
> Неправильно. > > Сделай функцию, которая принимает строку подключения, а > вернуть должна экземпляр коннекта. > Пусть функция создаёт экземпляр коннекта, настраивает параметры > подключения и передаёт экземпляр в поток для открытия соединения. > > Потом функция пусть отсчитывает таймаут, и если поток не > завершился, то устанавливает потоку флажок "коннект уже > не нужен, удавись", и возвращяет NIL. > > А поток пусть ждёт подключения хоть до морковкина заговенья. > Когда процедура потока получит возврат управления из Connection. > Open(), она проверит флажок, если он сброшен, то просто > оставит экзкмпляр коннекта в покое, а если установлен, то > закроет соединение и разрушит экземпляр коннекта. > В обоих исходах поток, в конце-концов, самоубъётся апстену. > > > Не надо никаких разных потоков для подключения и уничтожения > коннектов.
Спасибо, попробую.
-
> Но вот почему в процедуре Execute не так все хорошо удаляется. другой поток, не инициализирована COM модель, обьект завязан на "чужую"... что хорошего может быть?
> Спасибо, попробую. а нормально, на асинхронной/событийной логике слабо?
-
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;
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.
-
> //из основного потока по > //этой переменной узнает что подключение есть по данному коду его вообще никогда не будет... можно не парится с узнаванием.
+ создание потока и деструктор делаются в основном потоке вообще то... т.что в принципе ты ничего не изменил.
++ а где Free?
-
> а где Free? а, нашел.
-
Поток создается в основном потоке, тут я только код самого потока подключения привел как советовал DiamondShark.
> создание потока и деструктор делаются в основном потоке > вообще то... т.что в принципе ты ничего не изменил.
Деструктор чего, потока или коннекта. Если потока, то для этого FreeOnTerminate в True установлено. Деструктор будет вызван когда закончиться выполнение процедуры Execute.
-
> Если потока, то для этого FreeOnTerminate в True установлено. при чем здесь FreeOnTerminate, у тебя опять CoInitialize делается не в том потоке в котором используется...
-
вот для примера 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;
FreeOnTerminate:= true;
Resume;
end;
destructor TConnThread.Destroy;
begin
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).
-
> TConnThread = class(TThread) ??? а как же декларируемая независимость от основного потока?
-
> у тебя опять CoInitialize делается не в том потоке в котором > используется
мне кажется, должно быть CoInitializeEx (...COINIT_MULTITHREADED)
-
> работает? а теперь закоментарь внутри Execute и раскоментарь > в деструкторе/конструкторе... работает? или AV? (что возможно > при "пересечениях" потоков. ну или у тебя очень быстрый > комп... успевает, добавь нагрузку в Execute).
У меня и раньше память высвобождалась занятая коннектом, количество хендлов оставалось, прежним. При создании увеливались, а при уничтожении не удалялись.
> мне кажется, должно быть CoInitializeEx (...COINIT_MULTITHREADED)
Спасибо, надо почитать мануалы.
-
> мне кажется, должно быть 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 от мелкософта... ;) > У меня и раньше память ... бла.бла.бла. чем дальше, тем больше ощущение, что я тебе "про бабушку", а ты все "про дедушку" (уж куда более очевидно, проверил, да/нет ситуация такая, нет опять фигню свою несет)... и кому это надо? парься сам.
-
Игорь Шевченко © (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;
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
Connection.ConnectionString:= 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\;Extended Properties=Text'; Connection.Connected:= true;
end;
end. что делать понятно... 2 варианта.
-
sniknik © (03.08.11 20:45) [37]
> без разницы
CoInitialize calls CoInitializeEx and specifies the concurrency model as single-thread apartment.
с разницей.
> т.е. любом случае не обслуживает несколько потоков одной > инициализацией
CoInitializeEx должна быть вызвана в самом потоке, который работает с OLE, COM, ADO, etc
-
> с разницей. блин. - не ходите дети через дорогу на красный может сбить машиной! - а вроде тут ходят троллейбусы... - да без разницы чем собьет. убить может. - с разницей. типа машина на бензине, а троллейбусы на электричестве... и это ОЧЕНЬ важно, просто чтобы возразить. хотя говорили о другом.
> CoInitializeEx должна быть вызвана в самом потоке, который работает с OLE, COM, ADO, etc а я о чем? с самого начала. ты читай, что пишут прежде чем возражать... повторенными словами оппонента.
-
скажите, а procedure CoUninitialize; external ole32 name 'CoUninitialize'; обязательно вызывать, если поток где я вызываю CoInitializeEx(nil, 0); мне больше не нужен?
-
по msdn вызовы должны быть строго парные. освобождение ресурсов... т.е. раз вызвал CoInitialize то неважно, что тебе нужно, должен вызвать и CoUninitialize.
|