| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Delphi: Общие вопросы > Помогите "временно" заблокировать процедуру |
| Автор: oleg153 4.5.2009, 19:50 | ||||
| Есть 3 приложения : - Клиент (1) - Сервер-Клиент (2) - Сервер (3) Основное - (2) . Описаны две процедуры которые работают с MSSQL через ADO. Процедура-2 вызывается Клиентом(1) , Процедура-1 вызывается Сервер-Клиентом (2). Обе процедуры исполняются на Сервер-Клиенте. В процесе исполнения Процедуры-1 , Сервер (2) - обращается к внешнему Серверу(3) . Проблемма в том что НИКАК ни через Критич. Секции, Ни через Мьютексы не могу заставить процедуру-2, ждать пока не отработается процедура-1. Как только я вызываю метод реализованый на Сервере (3) вся защита слетает и начинает отрабатываться процедура-1 запущеная Клиентом.
В общем как доходит Процедура-1 до DServ.Test1(7000); Вся защита срывается и начинает отрабатываться procedure TForm1.SetData2 которая в этот момент ждет исполнения вызваная Клиентом (1).
Мне НУЖНО чтоб Процедура-1 отработалась до конца и только потом запустилась ждущая Процедура-2 Подскажите КАК это сделать. Бьюсь уже вторые сутки.... |
| Автор: MetalFan 5.5.2009, 08:34 |
| что-то прям чудеса какие-то. на первый взгляд работа с мютексами вроде бы корректная. |
| Автор: CodeMonkey 5.5.2009, 09:06 |
| Может и глупость скажу, но... у вас процедура-1 и процедура-2 выполняются в разных потоках? Добавьте логгинг GetCurrentThreadId. Если в одинаковых, то, скорее всего, ситуация выглядит как-то так: запускается процедура-1, берёт блокировку, где-то внутри себя вызывает Application.ProcessMessages (или аналог) - в этот момент запускается сидящая в очереди процедура-2, она работает, поскольку текущий поток уже владеет блокиовкой, процедура-2 завершается, затем заканчивается процедура-1 и отпускает блок. |
| Автор: oleg153 5.5.2009, 13:15 |
| Спасибо. Кое что понял Я проверил - по Memo1.Lines.Add(inttostr(GetCurrentThreadId()); получается что поток один. Я из того что читал понял что VCL (основной поток) и поток COM - разные, даже если сервер создан как STA. Но похоже я ошибся. Значит все варианты блокировки с использованием секций и мьютексов не пройдут. Или нужно создавать отдельные потоки Но почему запускается Процедура-2 ? Ведь вроде смысл одного потока - ждать окончания 1-ой процедуры ? И что делать если НЕ разделять процедуры по разным потокам ? Как не позволить запускаться |
| Автор: CodeMonkey 5.5.2009, 13:46 | ||
Если вы привели полный код, то вот это у вас кто: DServ.Test1? Ещё ShowMessage содержит цикл Application.ProcessMessages (если это действительно причина). |
| Автор: oleg153 5.5.2009, 14:01 | ||
DServ - соответственно Сервер-3. Еще один сом-сервер также реализованый как STA (однопотоковый) , который производит обработку переданых ему в процедуре SetData1 - данных. В Данном примере я на сервере-3 просто организовал задержку и передаю время задержки. Это для тестирования. В реали я передаю данные которые обрабатываются 0.5-5 сек и возвращают результат обработки. Возврат не стал делать так как его наличие не влияет на результат, все равно в потоке после вызова DServ.Test1 начинает работать Процедура-2. Т.е. в основном потоке очередь от Процедуры-1 передается на П-2 хотя П-1 не отработалась до конца. К сожалению [B][Ещё ShowMessage содержит цикл Application.ProcessMessages (если это действительно причина)./B] - это мне не понятно. Если можно поясните. |
| Автор: CodeMonkey 5.5.2009, 15:33 |
| Я, признаться, в COM полный 0, поэтому при ваших объяснениях сразу забуксовал. Давайте вам кто-нибудь другой что-нить скажет |
| Автор: Palladin 5.5.2009, 20:55 | ||
| Мдя, ну на крайняк: Создаешь переменную, обнуляешь её, когда заканчивается первая процедура присваеваешь переменной, ну к примеру значение '1', а весь код второй процедуры заключаешь в
Добавлено через 3 минуты и 41 секунду Не прочитал все, поэтому сверху чушь. Однако, вот ты передаешь значения в первой процедуре, во второй попробуй проверять, ну мол передалось ли последнее значение первой процедуры, если да то выполняем вторую процедуру, если нет... Получается тоже if(a=1) только вместо а проверяй мол ввелось ли или передалось ли чтонить... |
| Автор: oleg153 6.5.2009, 11:29 |
| В общем вроде понял. У меня был Сервер-2 однопотоковый. Соответственно ни секции ни мьютексы не работали. Сом-методы вызывались в основном, и захват Крит.секции этим потоком позволял выполнять как процедуры вызваные Сервером, так и через СОМ. Сделал его в модели Free - т.е. многопотоковым. Тогда Клинт и Сервер могут запускать одну процедуру в разных потоках и можно использовать крит. секцию. Но если в сом-потоке будет присутствовать обращение к VCL элементам Сервера-2 , тогда все просто встанет, так как для этого нужно передать управление основному потоку. Соответственно сом-поток ( в апартаменте) не может завершить действия. В общем , чтоб не городить флаги, придется делать многопотоковый сервер и просто внимательно следить за обращением к VCL-компонентам. Обращаться к ним только из основного потока, когда он владеет Крит.секцией. Пока на первый взгляд так должно работать. В вышеприведенном коде вроде работает. |
| Автор: CodeMonkey 6.5.2009, 13:20 |
| Ну, для обращения к главному потоку из любого вторичного есть Synchronize (также и в статическом варианте), либо удобная оболочка для него EnterMainThread/LeaveMainThread из AsyncCalls. |
| Автор: oleg153 6.5.2009, 15:07 |
| Если можно, проясните, сам пока не понял. Есть Основной поток Сервера и COM-поток созданный Сервером Для исключения доступа к процедуре описаной в главной форме использую Крит. секцию. СОМ-поток захватывает секцию и работает с Таблицами (MSSQL) через ADO. Если в момент работы когда Крит.сек. захвачена Основной поток пытается запустить процедуру, он натыкается на блокировку через Крит.секс. и становится в ожидание. И вот если в этот момент активный СОМ-поток пытается что либо сделать с экранными компонентами (Memo, DBGrid ) - тогда все умирает. Совсем. Тупик. Понимаю что это из-за блокировки основного потока Критической секцией. Но поможет ли здесь Synchronize ? Ведь основной поток заблокирован на попытке EnterCriticalSection(CS); Подскажите, так как с потоками только разбираюсь и боюсь напортачить. |
| Автор: CodeMonkey 6.5.2009, 16:01 | ||
Мне просто показалось, что обе процедуры запускаются из вторичных потоков. А так, вы, конечно, правы - у вас будет dead-lock - два потока будут ждать друг-друга. Если из главного потока нужно запустить что-то, что требует блокировки, которую может держать другой поток, который может запросить главный поток, который живёт в доме, который построил джек, то лучше бы это "что-то" запустить в ещё одном отдельном потоке, дабы не блокировать главный. Блокировать главный поток в VCL-приложении - это не очень хорошо. Если пойдёте таким путём, то к вашим услугам Synchronize. Если же нет - то тут только отправка сообщений. Да и то, сообщение не будет обработано, пока главный поток блокирован. Так что, ситуация с dead-lock-ом может даже и повториться, если вы используете SendMessage. |
| Автор: oleg153 6.5.2009, 16:18 |
| Да. Спасибо. Я Тоже прихожу к выводу что придется запускать в отдельном, не COM потоке. Теперь осталось внимательно разобратся с потоками. |
| Автор: oleg153 6.5.2009, 17:32 | ||
| Пока еще не понял как совместить Synchronize и ADO Допустим в потоке нужно выполнить код :
Здесь ADOQuery через TDataSource отображает в таблице DBGrid результаты. Причем динамически. Это можно увязать с Synchronize ? |
| Автор: MetalFan 7.5.2009, 11:46 | ||
ого, какая жесть... из доп. потока идет работа с компонентами (ADO), что лежат на форме, созданной и показываемой в осн.потоке?! тут вижу два варианта: 1. создавать ADO компоненты (для получения данных) в потоке, а потом "отдавать" их осн.потоку, чтобы он "подкючил" их для отображения через DataSource. 2. Отключать, на время работы с базой в доп.потоке, "визуализаторы" данных... |