Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2012.05.13;
Скачать: [xml.tar.bz2];

Вниз

TThread + Synchronize = bug   Найти похожие ветки 

 
abb777 ©   (2010-12-19 21:25) [0]

Hi, All,

Не могу понять, в чем дело. Вроде все, как надо, сделал.
Есть несколько одинаковых нитей, взаимодействующих через события (CreateEvent() / WaitForSingleObject() ) друг с другом. В методе Execute вызывается Synchronize() для отображения хода выполнения каждой нити.

Все работает прекрасно, если запустить эту систему нитей и дать ей доработать до конца. В конце все нити уничтожаются, все TThread-ы уничтожаются, все на ура.

Проблема в принудительном останове. Все виснет, если в основной нити VCL просто вызвать нечто типа

 for i := 0 to FLayers.Count - 1 do
 begin
   FLayers.Layer[i].FThread.Terminate;
 end;


Данный код просто завешивает все нити. Они висят, как видно в отладочном окне "Threads" на некоем вызове WaitForSingleObject(), но, насколько я понял, это не "мой" WaitForSingleObject() из метода Execute(), а некий другой. Внутри Execute() все с виду ОК - Terminated там анализируется. Да и дело, похоже не в этом. Если я закомментарю вызов Synchronize(), то все останавливается ОК, все нити завершаются, как только я выйду из кода, приведенного выше.

Исходя из этого факта я предположил, что зависание происходит в коде самого TThread, а именно здесь:

class procedure TThread.Synchronize(ASyncRec: PSynchronizeRecord);
..............
       LeaveCriticalSection(ThreadLock);
       try
         WaitForSingleObject(SyncProc.Signal, INFINITE); <<<<<<<<<
       finally
         EnterCriticalSection(ThreadLock);
       end;
...................


Перепробовал кучу всяких изворотов, убрал Synchronize() из этих нитей, которых может быть и 100 (сейчас только 10), и создал спец. нить для отображения, а всю информацию для отображения "рабочие" нити валят в список, который разгребает эта "нить отображения". Тот же эффект. Все работает и завершается по окончанию метода Execute() нормально, но при принудительно/досрочном завершении все виснет. Причем виснут все нити, а не только та одна, где сейчас есть вызов Synchronize.

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

Кто-нибудь такое встречал?
Есть идеи?

Заранее спасибо.


 
Leonid Troyanovsky ©   (2010-12-19 21:38) [1]


> abb777 ©   (19.12.10 21:25)  

> Есть идеи?

Ошибка в 17 строке.

--
Regards, LVT.


 
Dimka Maslov ©   (2010-12-19 22:00) [2]

После остановки потока во время работы метода synchronize в деструкторе потока происходит завешивание основного потока при выполнении WaitForSingleObject, который ждёт заершения уже завершённого потока.


 
_Юрий   (2010-12-19 22:34) [3]


>   LeaveCriticalSection(ThreadLock);
>        try
>          WaitForSingleObject(SyncProc.Signal, INFINITE);
>  <<<<<<<<<
>        finally
>          EnterCriticalSection(ThreadLock);
>        end;


Здесь дед лок.
Крит секции не нужны, уберите


 
abb777 ©   (2010-12-19 22:34) [4]

Мало кода? ОК. Держите. Только будет ли кто-то в этом разбираться?... Терзают смутные сомнения... Я надеялся, что кто-нить встречал такой СИМПТОМ.

Проблема возникает при вызове  TREngine.CancelIteration.
Такие строчки, как   if FRCS.DebugInfo = nil then Exit; или if Self = nil then Exit; вставлены чисто для отладки, так что за них пинать не надо, я надеюсь, что суть не в них.

Привести весь код не позволяет форум.


procedure TLayerThread.Execute;
begin
 EnterCriticalSection(FRCS);
 FLayer.FCurStep := 0;
 FLayer.FIsActive := true;
 FLayer.FIsReady := false;
 FLayer.FIsWaiting := false;
 LeaveCriticalSection(FRCS);
 while true do
 begin
   // принудительный выход
   if Terminated or
      FLayer.FTerminating or
      (FLayer.FEvent = INVALID_HANDLE_VALUE) then
   begin
     FLayer.FTerminating := true;
     break;
   end;
   // флаг реверсивности
   Ffr := FLayer.FCurZone.FIsReversed;
   // флаг ожидания - истина для всех внутренних слоев или
   // для нижнего слоя при реверсивной подаче
   // для верхнего слоя при нормальной подаче
   Fwu := FLayer.FUpperLayer <> nil;
   Fwl := FLayer.FLowerLayer <> nil;
   Ffw := (Fwu or not Ffr) and (Fwl or Ffr);
   if Ffw then
   begin
     Fres := WAIT_TIMEOUT;
     repeat
       //
       if Terminated or
          FLayer.FTerminating or
          (FLayer.FEvent = INVALID_HANDLE_VALUE) then
       begin
         FLayer.FTerminating := true;
         break;
       end;
       //
       if Fwu and Ffr then
       begin
         EnterCriticalSection(FRCS);
         if (FLayer.FUpperLayer.FCurStep > FLayer.FCurStep) or
            (FLayer.FUpperLayer.FCurStep = FLayer.FCurStep) and
            (FLayer.FUpperLayer.FIsReady) then
         begin
           Ffw := false;
           ResetEvent(FLayer.FEvent);
         end;
         LeaveCriticalSection(FRCS);
       end           else
       if Fwl and not Ffr then
       begin
         EnterCriticalSection(FRCS);
         if (FLayer.FLowerLayer.FCurStep > FLayer.FCurStep) or
            (FLayer.FLowerLayer.FCurStep = FLayer.FCurStep) and
            (FLayer.FLowerLayer.FIsReady) then
         begin
           Ffw := false;
           ResetEvent(FLayer.FEvent);
         end;
         LeaveCriticalSection(FRCS);
       end;
       if Ffw then
       begin
         FLayer.FIsWaiting := true;
         Fres := WaitForSingleObject(FLayer.FEvent, 100);
         FLayer.FIsWaiting := false;
       end    else
       begin
         break;
       end;
     until Fres <> WAIT_TIMEOUT;
   end;
   if Terminated or
      FLayer.FTerminating or
      (FLayer.FEvent = INVALID_HANDLE_VALUE) then
   begin
    FLayer.FTerminating := true;
    break;
   end;
   //
   FLayer.CalculateState1;
   //
   if Terminated or
      FLayer.FTerminating or
      (FLayer.FEvent = INVALID_HANDLE_VALUE) then
   begin
     FLayer.FTerminating := true;
     break;
   end;
   //
   EnterCriticalSection(FRCS);
   FLayer.FIsReady := true;
   if Ffr then
     if Fwl and
        (FLayer.FLowerLayer.FCurStep = FLayer.FCurStep) and
        (FLayer.FLowerLayer.FEvent <> INVALID_HANDLE_VALUE) then
       SetEvent(FLayer.FLowerLayer.FEvent)
     else
   else
     if Fwu and
        (FLayer.FUpperLayer.FCurStep = FLayer.FCurStep) and
        (FLayer.FUpperLayer.FEvent <> INVALID_HANDLE_VALUE) then
       SetEvent(FLayer.FUpperLayer.FEvent);
   LeaveCriticalSection(FRCS);
   //
   FLayer.CalculateState2;
   if Terminated or
      FLayer.FTerminating or
      (FLayer.FEvent = INVALID_HANDLE_VALUE) then
   begin
      FLayer.FTerminating := true;
     break;
   end;
   //
   EnterCriticalSection(FRCS);
   if not FLayer.FTerminating then
     FLayer.FEngine.FDispList.Add(TREDisplayRec.Create(FLayer, FLayer.FCurStep));
   LeaveCriticalSection(FRCS);
   //
   if FLayer.FCurStep < FLayer.FList.Count - 1 then
   begin
     EnterCriticalSection(FRCS);
     Inc(FLayer.FCurStep);
     FLayer.FIsReady := false;
     LeaveCriticalSection(FRCS);
   end                                         else
   begin
     break;
   end;
 end;
 FLayer.FIsActive := false;
end;

procedure TREngine.CancelIteration;
var
 i : integer;
 f : boolean;
begin
 for i := 0 to FLayers.Count - 1 do
 begin
   FLayers.Layer[i].FTerminating := true;
   FLayers.Layer[i].FThread.Terminate;
 end;
 EnterCriticalSection(FRCS);
 FDispList.Clear;
 if FDispThread <> nil then
   FDispThread.Terminate;
 LeaveCriticalSection(FRCS);
 Sleep(0);
 repeat
   f := true;
   for i := 0 to FLayers.Count - 1 do
    if FLayers.Layer[i].FIsActive then
    begin
      f := false;
      break;
    end;
   Sleep(0);
 until f;
end;

procedure TREngine.DoDisplay;
var
 i : integer;
begin
 if FRCS.DebugInfo = nil then Exit;
 if Assigned(FOnStep) then
 begin
   begin
     EnterCriticalSection(FRCS);
     for i := 0 to FDispList.Count - 1 do
     begin
       FOnStep(Self,
               FDispList.DisplayRec[i].FLayer,
               FDispList.DisplayRec[i].FCurStep);
     end;
     FDispList.Clear;
     LeaveCriticalSection(FRCS);
   end;
 end;
end;


 
_Юрий   (2010-12-19 22:40) [5]

да, это я мощно задвинул
бываит :-)


 
abb777 ©   (2010-12-19 22:41) [6]


> Здесь дед лок.
> Крит секции не нужны, уберите

Откуда? Это кусок из Classes.pas.

У меня крит. секции используются только там, где надо. Я так думаю.

А насчет

После остановки потока во время работы метода synchronize в деструкторе потока происходит завешивание основного потока при выполнении WaitForSingleObject, который ждёт заершения уже завершённого потока.

Так вопрос как раз в том и есть, как это сделать правильно, хотя я нигде не могу найти никаких упоминаний о каких-либо подводных камнях использования Synchronize.


 
Leonid Troyanovsky ©   (2010-12-19 23:04) [7]


> abb777 ©   (19.12.10 22:34) [4]

> Привести весь код не позволяет форум.

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

--
Regards, LVT.


 
abb777 ©   (2010-12-19 23:19) [8]

>> Привести весь код не позволяет форум.

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

А я как раз об этом. Весь проект я приводить и не собирался.

Попробовал использовать CheckSynchronize(0); в начале CancelIteration(). Если прервать процесс почти сразу после начала, то сбой 100%, а если подождать секунд 5-6, а затем прервать, то выходит корректно. Внутри Execute с виду никой специфики нет в зависимости от числа шагов. В качестве эмулятора расчетной процедуры используется ГСЧ типа


procedure CalculateState;
var
 x : Double;
begin
 while true do
 begin
   x := Random;
   if (x > 0.5) and (x < 0.501) then break;
 end;
end;


 
Дмитрий Белькевич   (2010-12-20 09:21) [9]

>FTerminating

Зачем этот флаг? Terminated треда не достаточно?

>Synchronize

Не вижу в коде ни одного.


 
Дмитрий Белькевич   (2010-12-20 09:27) [10]

>Если я закомментарю вызов Synchronize(),

Что если попробовать вместо Terminate связку Terminate + WaitFor?


 
Leonid Troyanovsky ©   (2010-12-20 10:02) [11]


> abb777 ©   (19.12.10 23:19) [8]

> А я как раз об этом. Весь проект я приводить и не собирался.

Ну, а где ж достаточность? Под достаточностью подразумеваем:
если уж нашелся желающий воспроизвести пример, то он
не придется гадать, что такое TREngine, FLayer и куда засунуть
procedure CalculateState, а сможет легким движением руки
скопировать код и откомпилировать у себя.

--
Regards, LVT.


 
Cobalt ©   (2010-12-20 13:22) [12]

я так подозреваю, что ошибка у вас в коде, выполняющемся после
for i := 0 to FLayers.Count - 1 do
begin
  FLayers.Layer[i].FThread.Terminate;
end;


 
abb777 ©   (2010-12-20 13:37) [13]

Specially for LVT--
Я понимаю, и уже давно, все, что Вы, Леонид, хотите сказать. Но для того, чтобы сделать компиляцию легким движением нужен специальный тестовый проект, поскольку в реальном используются другие модули, откуда берется куча всяких переменных. Я это сделаю, но только в том случае, если не услышу никаких советов от опытных людей, реально строивших многопоточные программы. И в любом случае разместить этот тест на форуме просто нереально. Но я дам урл, откуда его скачать.

To Дмитрий Белькевич:

>>FTerminating
>Зачем этот флаг? Terminated треда не достаточно?

Наследство от отладки. Я пытался сначала проставлением этого флага вывести каждый Execute() на самый конец, где уже нет никаких WaitForSingleObject(), а есть только цикл типа

while not Terminated do
begin
 Sleep(0);
end;


а уже затем последовательно вызвать Terminate для каждой нити и дождаться в цикле пока все нити закроются. Я не мог понять, на каком-таком WaitForSingleObject() зависают все нити. Я сначала думал, что это в моем алгоритме что-то глючит. Потом догадался, что это не совсем у меня.

>>Synchronize
>Не вижу в коде ни одного.

Пардон. Я выпустил это кусок из виду. Я же говорю, форум не дает запостить весь модуль целиком. Вот это место:

constructor TREDisplayThread.Create(AEngine : TREngine);
begin
 inherited Create(true);
 FEngine := AEngine;
 FreeOnTerminate := true;
 Priority := tpIdle;
 Resume;
end;

destructor TREDisplayThread.Destroy;
begin
 if FEngine <> nil then
   FEngine.FDispThread := nil;
 inherited Destroy;
end;

procedure TREDisplayThread.Execute;
begin
 while not Terminated do
 begin
   Synchronize(FEngine.DoDisplay);
   Sleep(0);
 end;
end;


Сам объект TREDisplayThread -- это поле TREngine.FDispThread. Создается в TREngine.NewIteration;, уничтожается в TREngine.CancelIteration. Раньше Synchronize() был в каждой "рабочей" нити TLayerThread. Затем я сделал для него отдельную нить. Не особо помогло :(

> Что если попробовать вместо Terminate связку Terminate + WaitFor?

А код конкретный нельзя ли привести?
И куда это вставить?


 
Ega23 ©   (2010-12-20 13:38) [14]


> Все работает и завершается по окончанию метода Execute()
> нормально, но при принудительно/досрочном завершении все
> виснет.


1. В свой Execute вставляй частую проверку if Terminated then ...
2. Откажись от Synchronize, используй SendMessage


 
Ega23 ©   (2010-12-20 13:40) [15]

if FEngine <> nil then
  FEngine.FDispThread := nil;

Вот это вот не понял.


 
abb777 ©   (2010-12-20 13:47) [16]

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

А что конкретно не так? Я там пробовал столько всяких вариантов... Включал все в крит. секцию, делал без секции, пробовал завершать и дожидаться завершения каждой нити по порядку... Ничего не помогло. Единственное, что как-то сказалось на результате - это вставка CheckSynchronized() в самое начало метода CancelIteration(). Но тоже как-то все очень нестабильно.

Вопрос-то вроде бы просто формулируется - как мне убедиться из своего кода, что все Synchronize() уже проработали? И почему об этом требовании нигде не написано? Я, по крайней мере, нигде такого требования не нашел.


 
abb777 ©   (2010-12-20 13:53) [17]

>> if FEngine <> nil then
>>   FEngine.FDispThread := nil;
> Вот это вот не понял.

Это для того, чтобы внутри TREngine не вызывать FDispThread.Terminate, если она на данный момент уже завершилась. Чисто для страховки объект говорит своему родителю - "меня уже нет".


 
abb777 ©   (2010-12-20 14:00) [18]

> 1. В свой Execute вставляй частую проверку if Terminated then ...

Так я уже и так там навставлял кучу раз. Во всех циклах и после каждой затяжной процедуры. Куда уж чаще...

> 2. Откажись от Synchronize, используй SendMessage

Вот с этого момента поподробней. Как может помочь SendMessage? Это не будет threadsafe, насколько я понимаю. Я пробовал использовать PostThreadMessage() но ничего не получил. Там нет Handle, а я не знаю, как ловить формой такие сообщения. Конкретный пример, вероятно, помог бы.


 
Ega23 ©   (2010-12-20 14:49) [19]


>  Это не будет threadsafe, насколько я понимаю.

Почему? Всё будет ровно так, как ты напишешь.

> Там нет Handle, а я не знаю, как ловить формой такие сообщения.

Ну у твоего объекта-то Handle есть? Есть. Вот ему и шли. Если нет - заведи.

>  Конкретный пример, вероятно, помог бы.

Ну я вечером из дома постараюсь пример привести, если силы будут.


 
han_malign   (2010-12-20 15:07) [20]


> Так вопрос как раз в том и есть, как это сделать правильно

- правильно - не тормозить расчеты ожиданием синхронизации с основным потоком...

В некоторых случаях лучше делать опрос состояния потоков из основного(по таймеру, или в Application.OnIdle, или в PostMessage нотификации).

Чтобы не тормозить потоки на получении состояния:
1. в критическую секцию надо оборачивать только изменение/получение описательной части состояния. Лучше сделать описательную часть состояния атомарной - выделить в структуру, обновляющуюся в узловых точках алгоритма.
2. "длинные счетчики `реального времени`" масштабировать на процентную шкалу, с InterlockedXxx синхронизацией только по определённому порогу(Memory Barrier - синхронизация каждой итерации может дать серьезное пенальти производительности).
3. Проверять необходимость "большой синхронизации" по короткому штампу состояния(например счетчик изменений + последнее считанное изменение, или тупой самосбрасываемый Changed-флаг; семафор - для мягкого реального времени с пропуском кадров визуализации - не очень подходит)
4. Дополнительно урезать период опроса проверкой какого либо счетчика реального времени(обычно достаточно GetTickCount). Даже в случае таймера(с пропуском лишних WM_TIMER), если время обработки состояния сравнимо с периодом таймера, возможно уплотнение шкалы опроса(следствие из теоремы Котельникова), не говоря об OnIdle и переполнении стека на не проверяемых PostMessage...
5. И никаких Repaint - фиксация состояния + Invalidate, если за то время пока дойдет до OnPaint, состояние еще несколько раз изменится - то это единственный правильный сценарий для системы мягкого реального времени...

З.Ы. Если у вас самоцель полчаса любоваться на каждый кадр состояния, вместо того, чтобы завершить расчет за 15 секунд - все таки подумайте о логах...


 
clickmaker ©   (2010-12-20 15:57) [21]

SendMessage тоже синхронный вызов. Лучше PostMessage


 
abb777 ©   (2010-12-20 16:24) [22]


> Почему? Всё будет ровно так, как ты напишешь.


Слушайте, давайте без флуда. И так уже голова пухнет...


> >> Там нет Handle, а я не знаю, как ловить формой такие
> сообщения.
> > Ну у твоего объекта-то Handle есть? Есть. Вот ему и шли.
>  Если нет - заведи.


Это вообще о чем? Посмотрите на формат вызова PostThreadMessage() и еще раз скажите, так, чтобы было понятно. Насчет "завести хендл" - надо заводить не просто THandle, а оконную процедуру. Это уже совсем другая песня, хотя я буду смотреть в эту сторону, если проблему с Synchronize() решить не удастся.


> >  Конкретный пример, вероятно, помог бы.
>  Ну я вечером из дома постараюсь пример привести, если силы
> будут.


Вот это уже по-деловому. Спасибо.


> SendMessage тоже синхронный вызов. Лучше PostMessage


Вот и я как раз об этом(см. выше). Поэтому не "Лучше", а "Только". Но куда посылать PostMessage? И к тому же, это точно установлено, что PostMessage() is threadsafe? И как конкретно надо это оформлять? Надо ли этот вызов окружать критической секцией?


 
clickmaker ©   (2010-12-20 16:43) [23]

> И к тому же, это точно установлено, что PostMessage() is
> threadsafe? И как конкретно надо это оформлять? Надо ли
> этот вызов окружать критической секцией?

что значит threadsafe? поскольку вызов асинхронный, должна быть гарантия, что данные, которые доступны на момент отправки, будут также доступны и в момент получения. т.е. исключаются локальные переменные и глобальные, которые могут быть изменены другими потоками

> куда посылать PostMessage?

окну-получателю. Если окна нет, должен быть поток с циклом выборки сообщений, и тогда PostThreadMessage ему


 
abb777 ©   (2010-12-20 16:53) [24]

Чисто для большего понимания:

To han_malign:

> - правильно - не тормозить расчеты ожиданием синхронизации
> с основным потоком...
>

У меня "рабочие" нити не тормозятся выдачей состояния. Тот WaitForSingleObject(), который там есть, нужен совсем для другого, и его наличие принципиально для решения поставленной задачи проекта. У нити, отвечающей за выдачу состояния расчета, стоит приоритет Idle, что практически эквивалентно Вашему предложению, насколько я понимаю.

> 1...........

У меня так, практически, и есть. Учитывая, что расчетные процедуры занимают времени 1000.1 по сравнению с добавлением элемента в список для отображения.

> 2..........

Насколько я понял, это типа того, что прежде, чем обновлять прогресс-индикатор, надо убедиться, что значение процента изменилось, а не тупо вызывать это обновление при каждой итерации цикла. Если да, то я всегда так и делаю.

> 3..........
> 4...........

Извините, но просто не понял. Если можно, на примере объясните, пожалуйста.

> 5............

До этого я точно не дойду. В событии формы, где собственно отображается прогресс стоит только Application.ProcessMessages, иначе ничего не отображается, видимо, потому, что поскольку процессор загружен почти на 100% во время работы всего этого хозяйства. Если это неправильно, то скажите, пожалуйста, как будет правильно.


 
abb777 ©   (2010-12-20 17:07) [25]

Кстати...

To han_malign:

> > - правильно - не тормозить расчеты ожиданием синхронизации
> > с основным потоком...
>

Так вроде Synchronize() не тормозит нить. Весь его смысл в этом и есть. Он берет указатель на метод и нить идет своим путем, а специальная нить Thread_Lock (вроде так) ждет, пока наступит очередь основной нити процесса, и выполняет этот метод там. В результате как раз и возникает проблема, что на момент выполнения Synchronize() в основной нити родительская нить Synchronize может уже завершиться. Именно в этом случае и возникает завсон. Я не прав?


 
Leonid Troyanovsky ©   (2010-12-20 18:14) [26]


> abb777 ©   (20.12.10 13:37) [13]

> Я понимаю, и уже давно, все, что Вы, Леонид, хотите сказать

Многие могли бы рассказать про многопоточные приложения.
Но, пока, увы, и обсуждать нечего.
Кроме туманных симптомов.

Да и уровень подготовки слушателей тоже немного смущает.

--
Regards, LVT.


 
Ega23 ©   (2010-12-20 19:22) [27]


> Так вроде Synchronize() не тормозит нить. Весь его смысл
> в этом и есть. Он берет указатель на метод и нить идет своим
> путем, а специальная нить Thread_Lock (вроде так) ждет,
> пока наступит очередь основной нити процесса, и выполняет
> этот метод там. В результате как раз и возникает проблема,
>  что на момент выполнения Synchronize() в основной нити
> родительская нить Synchronize может уже завершиться. Именно
> в этом случае и возникает завсон. Я не прав?


Нет, ты не прав. TThread.Syncronize + F1


> Слушайте, давайте без флуда. И так уже голова пухнет...

Ну как хочешь. Разбирайся сам.


 
Ega23 ©   (2010-12-20 19:23) [28]


> SendMessage тоже синхронный вызов. Лучше PostMessage


Это если дальше выполнять надо.
Мне в этом плане больше SendMessage нравится, в отличие от Syncronize, туда ещё и параметры можно положить.


 
abb777 ©   (2010-12-20 22:30) [29]

> Ну как хочешь. Разбирайся сам.

Извините. Я не хотел никого обидеть, но когда на конкретный вопрос получаешь философский ответ, становится немного грустно.

> Нет, ты не прав. TThread.Syncronize + F1

Да. действительно. Но тогда ранее полученный ответ про то, что Synchronize ждет уже завершенную нить, как-то не укладывается.

>>Мне в этом плане больше SendMessage нравится, в отличие от Syncronize, туда ещё и параметры можно положить.

SendMessage - это просто процедура, непосредственно выполняющая процедуру обработки сообщения. Выполняет она ее немедленно и в той же нити, в которой она вызывается, а отнюдь не в основной нити процесса. А требуется как раз это последнее. Иначе и копья ломать незачем. Можно и просто вызвать требуемый метод напрямую, а не через SendMessage. PostMessage же в принципе работает по-другому. Она лишь помещает сообщение в очередь, которую разгребает основная нить процесса. Поэтому PostMessage может заменить Synchronize, но меня смущает тот факт, что сообщения иногда пропускаются и не доходят до адресата. Synchronize выглядит более надежно.

ОК. попробую сделать тестовый пример...


 
Дмитрий Белькевич   (2010-12-20 23:20) [30]

То ли я не по-русски пишу, то ли одно из двух :)

"вместо Terminate связка Terminate + WaitFor"


for i := 0 to FLayers.Count - 1 do
begin
 FLayers.Layer[i].FThread.Terminate;
 FLayers.Layer[i].FThread.WaitFor;
end;


?


 
Германн ©   (2010-12-21 00:37) [31]


> SendMessage - это просто процедура, непосредственно выполняющая
> процедуру обработки сообщения. Выполняет она ее немедленно
> и в той же нити, в которой она вызывается, а отнюдь не в
> основной нити процесса.

Хм.


 
Ega23 ©   (2010-12-21 00:45) [32]


> Извините. Я не хотел никого обидеть, но когда на конкретный
> вопрос получаешь философский ответ, становится немного грустно.


Принято. Просто не забывай, что тебе никто ничего не должен. Это твоя проблема, а не кого-то там. И отвечающим на форуме никто не платит денег за их знания/время.
Это так, лирическое отступление.


> SendMessage - это просто процедура, непосредственно выполняющая
> процедуру обработки сообщения. Выполняет она ее немедленно
> и в той же нити, в которой она вызывается, а отнюдь не в
> основной нити процесса. А требуется как раз это последнее.
>  Иначе и копья ломать незачем. Можно и просто вызвать требуемый
> метод напрямую, а не через SendMessage. PostMessage же в
> принципе работает по-другому. Она лишь помещает сообщение
> в очередь, которую разгребает основная нить процесса. Поэтому
> PostMessage может заменить Synchronize, но меня смущает
> тот факт, что сообщения иногда пропускаются и не доходят
> до адресата.


The PostMessage function places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.


The SendMessage function sends the specified message to a window or windows. It calls the window procedure for the specified window and does not return until the window procedure has processed the message.


чуешь разницу?
Это означает, что если кто-то из какого-то потока послал некоему окну сообщение, то в первом случае (post) тот, кто послал - послал его и забыл про него. И ему пофигу, обработано оно или нет, он пошёл дальше.
Во втором случае (send) он не просто послал, а дождался подтверждения того, что сообщение было обработано. В целом это полностью соответствует методу Syncronize, за исключением того, что в Syncronize ты можешь передать метод без параметров, а в SendMessage присутствуют LParam и WParam, в которые ты можешь что-то запихнуть.


 
abb777 ©   (2010-12-21 01:30) [33]

> чуешь разницу?

Есессно :) Я выше изложил примерно то же самое, только другими словами. Я утверждаю, что SendMessage строго говоря вообще ничего не "посылает". Она просто вызывает соотв. оконную процедуру, и, естественно, выполняет ее, т.е. "дожидается ее завершения". Хотя и не ждет она совсем. Факт ожидания отсутствует, как таковой, в отличие от Synchronize(), где факт ожидания - в лице пресловутого WaitForSingleObject().

С PostMessage мне все было ясно, кроме одного: не сказано, а что делает та нить, которая разгребает очередь, в то время, когда наша нить постит сообщение. Это я и спрашивал выше. Надо окружать PostMessage крит. секцией или это винда разрулит без меня? Подозреваю, что последнее, но хотелось бы знать точно. А не написано.

В общем сделал я тест. Вроде ничего не поменялось. Жмем старт, дожидаемся, пока цифры сами остановятся, закрываем окно и оно закрывается. А если попробовать нажать стоп почти сразу после старта то курсор-часики. Ну или окно можно попробовать закрыть, что практически то же самое. Тест лежит тут:
http://home.bokovikov.com/etc/delphi/test.rar


> for i := 0 to FLayers.Count - 1 do
> begin
>  FLayers.Layer[i].FThread.Terminate;
>  FLayers.Layer[i].FThread.WaitFor;
> end;
>


ОК сейчас попробую. В пылу полемики я это пропустил. Извините.


 
abb777 ©   (2010-12-21 01:42) [34]

.... попробовал :( виснет так же. Зацикливается внутри TThread.WaitFor.


 
Ega23 ©   (2010-12-21 01:45) [35]


> Я утверждаю, что SendMessage строго говоря вообще ничего
> не "посылает".

Бляха-муха, я же тебе из хелпа конкретную выдержку привёл!
Не, ты конечно можешь утверждать всё, что угодно. Даже что Земля налетит на Небесную Ось. Про трёх слонов на черепахе, плывущей в бесконечном окияне - тоже можешь утверждать.
Что касается Syncronize.
Рекомендую вообще забыть про класс TThread и написать свой. Тогда многое станет понятным.


 
Германн ©   (2010-12-21 01:51) [36]


> Есессно :) Я выше изложил примерно то же самое, только другими
> словами. Я утверждаю, что SendMessage строго говоря вообще
> ничего не "посылает". Она просто вызывает соотв. оконную
> процедуру, и, естественно, выполняет ее, т.е. "дожидается
> ее завершения". Хотя и не ждет она совсем. Факт ожидания
> отсутствует, как таковой, в отличие от Synchronize(), где
> факт ожидания - в лице пресловутого WaitForSingleObject().
>
>

Бред.
Не надо излагать "другими словами". Нужно прочитать учебник.


 
han_malign   (2010-12-21 08:59) [37]


> Я утверждаю, что SendMessage строго говоря вообще ничего
> не "посылает". Она просто вызывает соотв. оконную процедуру,
>  и, естественно, выполняет ее, т.е. "дожидается ее завершения".

- но, "строго говоря", с одним маленьким незначительным нюансом:

SendMessage Function
...
Remarks
...
If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine. If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code. The sending thread is blocked until the receiving thread processes the message. However, the sending thread will process incoming nonqueued messages while waiting for its message to be processed. To prevent this, use SendMessageTimeout with SMTO_BLOCK set. For more information on nonqueued messages, see Nonqueued Messages.


> Мне в этом плане больше SendMessage нравится, в отличие
> от Syncronize, туда ещё и параметры можно положить.

- до D5 - Syncronize работал именно через SendMessage, но затем по каким то причинам перешли на собственную очередь(ну разве что - плюс в том что окно не нужно???)...


> Зацикливается внутри TThread.WaitFor.

вот это FreeOnTerminate := true; - забудь как страшный сон...

Если ты выставил этот флаг - обращаться к экземпляру объекта из другого потока ты не имеешь права(ни с Terminate, ни с WaitFor, ни с чем другим, за исключением контекста синхронного Callback-а(до выхода из которого поток гарантированно не помрет)), и должен вообще забыть что у тебя такая ссылка была, потому как ты его отпустил в вольное плавание, и он может в любой момент помахать ручкой...


 
abb777 ©   (2010-12-21 09:44) [38]

За открытие глаз относительно SendMessage отдельное спасибо. Действительно, балбес, не дочитал до конца справку...

Насчет FreeOnTerminate - так я же в критической секции это делал, когда все нити заведомо работали. Это только в последнем варианте я попробовал то, что мне предложили. И анализировал флаг окончания Execute нити, находящийся у ее родителя, прежде чем к ней обращаться.

> и он может в любой момент помахать ручкой

Тут уже спрашивали выше, зачем я из деструктора нити присваиваю nil ссылке на нить у ее родителя. Вот как раз для этого. Этого + крит. секции недостаточно?

И потом, если нить завершилась, а я к ней обращаюсь, то это как минимум приведет к AV, но никак не к зависанию "живой" нити, как я это вижу в реальности. Когда происходит глюк, то ссылки на все нити живые, что и приводит к зацикливанию в CancelIteration. Значит ни один из деструкторов этих нитей не сработал. Что-то концы с концами не сходятся.


 
Ega23 ©   (2010-12-21 10:33) [39]


> Тут уже спрашивали выше, зачем я из деструктора нити присваиваю
> nil ссылке на нить у ее родителя.


Вот для этого делаешь PostMessage из деструктора. И в LParam пихаешь Self. А в списке нитей берёшь IndexOf(TMyThread(LParam)) и удаляешь его. Не вызывая деструктор.


 
Дмитрий Белькевич   (2010-12-21 10:40) [40]


> .... попробовал :( виснет так же. Зацикливается внутри TThread.
> WaitFor.


Уже прогресс. Ищи, почему у тебя треды не останавливаются (не выходят из Execute).


 
Дмитрий Белькевич   (2010-12-21 10:41) [41]

>вот это FreeOnTerminate := true; - забудь как страшный сон...

лучше без этого, хотя если логика хорошо отработана - то можно.


 
Дмитрий Белькевич   (2010-12-21 10:45) [42]


> Зацикливается внутри TThread.> WaitFor.


Нет, не там. А в TThread.Execute.


 
abb777 ©   (2010-12-21 12:06) [43]


> > Зацикливается внутри TThread.> WaitFor.
>
> Нет, не там. А в TThread.Execute.

Ну может я и балбес, но я вижу где отладчик ходит. А в данном случае он ходит именно внутри WaitFor(). А все треды висят (видно в окошке Threads) на том же самом WaitForSingleObject() который ну никак не может быть моим, я это проверял выставляя перед ним флаг. Так что не крутятся мои треды внутри Execute, а именно стоят. Иначе окошко Threads показало бы другое.

После полученного просветления относительно SendMessage/PostMessage попробовал их. SendMessage работает точно так же, как и Synchronize() хотя где при этом треды гуляют или стоят, я не смотрел. Сразу попробовал PostMessage(). Оно сработало. Правда был конфликт - в обработчике события у формы был вставлен Application.ProcessMessages, что приводило к рекурсии и, как результату EListError в этом месте:


procedure TREngine.DoDisplay;
var
  i : integer;
begin
  if Assigned(FOnStep) then
  begin
    begin
      EnterCriticalSection(FRCS);
      for i := 0 to FDispList.Count - 1 do
        if i < FDispList.Count then
          FOnStep(Self,
                  FDispList.DisplayRec[i].FLayer,
                  FDispList.DisplayRec[i].FCurStep); <<<<<<<<<<<<<<<
      FDispList.Clear;
      LeaveCriticalSection(FRCS);
    end;
  end;
end;


Остался еще вопрос. Все работает с виду ОК, но только если и в вышеприведенном коде и в коде ниже вставлены крит. секции:


procedure TREngine.WndProc(var Msg: TMessage);
begin
  with Msg do
    if Msg = WM_RE_NOTIFY then
    begin
      DoDisplay;
      Result := 1;
    end                   else
    begin
      Result := DefWindowProc(FWND, Msg, WParam, LParam);
    end;
end;

procedure TREDisplayThread.Execute;
begin
  while not Terminated do
  begin
    EnterCriticalSection(FRCS);
    if FEngine.FDispList.Count > 0 then
      PostMessage(FEngine.FWND, WM_RE_NOTIFY, 0, 0);
    LeaveCriticalSection(FRCS);
    Sleep(0);
  end;
end;


Если убрать КС в TREDisplayThread.Execute, то отображение сильно тормозится. А в TREngine.DoDisplay() вообще убирать нельзя, иначе AV. Если насчет причины AV еще понятно, то насчет тормозов с отображением не очень. Можете объяснить причину? Просто интересно.


 
Ega23 ©   (2010-12-21 12:15) [44]

EnterCriticalSection(FRCS);
     for i := 0 to FDispList.Count - 1 do
       if i < FDispList.Count then
         FOnStep(Self,
                 FDispList.DisplayRec[i].FLayer,
                 FDispList.DisplayRec[i].FCurStep); <<<<<<<<<<<<<<<
     FDispList.Clear;
     LeaveCriticalSection(FRCS);


А вот и дедлок.
Запомни навсегда, выучи как Присягу:
EnterCriticalSection
try

finally
 LeaveCriticalSection
end;


Иначе любое исключение после входа приведёт к тому, что выхода не будет. И все будут ждать.


 
Ega23 ©   (2010-12-21 12:16) [45]


>  Sleep(0);


Не надо 0, поставь ну там 20, например.


 
abb777 ©   (2010-12-21 12:26) [46]

Спасибо, за ценное замечание. Хотите сказать, что это и есть причина первоначальной проблемы? Я, признаться, о таком не подумал. Там, с виду, негде было исключениям взяться. Сейчас везде вставлю.

После убирания Application.ProcessMessages из обработчика события у формы вариант с SendMessage() тоже работает, но вокруг него надо убрать крит. секцию. Иначе не работает.


 
abb777 ©   (2010-12-21 12:45) [47]

Поправил. С SendMessage() 15 сек полет нормальный...
А так можно (см. отмеченные строчки)?
procedure TREngine.CancelIteration;
var
 i : integer;
 f : boolean;
begin
 WaitOn;
 for i := 0 to FLayers.Count - 1 do
 begin
   EnterCriticalSection(FRCS);
   f := true;
   try
     if FLayers.Layer[i].FIsActive then
     begin
       FLayers.Layer[i].FThread.Terminate;
       LeaveCriticalSection(FRCS);
       f := false;
       while FLayers.Layer[i].FIsActive do
       begin
         Sleep(20);
       end;
     end                           else
     begin
       LeaveCriticalSection(FRCS);
       f := false;
     end;
   finally
     if f then                               <<<<<<<<<<<<<<<<
       LeaveCriticalSection(FRCS);  <<<<<<<<<<<<<<<<
   end;
 end;
..........................
end;


 
han_malign   (2010-12-21 12:47) [48]

так, к слову:
if ... (FLayer.FEvent = INVALID_HANDLE_VALUE) ...

- почитай Return Values для CreateEvent


 
abb777 ©   (2010-12-21 13:15) [49]

Ничего не сказано про результат в случае ошибки. Только про GetLastError.

У меня оно действительно не анализируется. Пока что не думал об этом. А процитированный код связан вот с этим:

destructor TRLayer.Destroy;
begin
 CancelIteration;
 if FEvent <> INVALID_HANDLE_VALUE then
 begin
   CloseHandle(FEvent);
   FEvent := INVALID_HANDLE_VALUE;
 end;


 
han_malign   (2010-12-21 13:15) [50]


> procedure TREDisplayThread.Execute;
> begin
>   while not Terminated do
>   begin
>     EnterCriticalSection(FRCS);
>     if FEngine.FDispList.Count > 0 then
>       PostMessage(FEngine.FWND, WM_RE_NOTIFY, 0, 0);
>     LeaveCriticalSection(FRCS);
>     Sleep(0);
>   end;
> end;

- выкидываешь нафик
procedure TLayerThread.Execute;
begin
  .........
  EnterCriticalSection(FRCS);
  if not FLayer.FTerminating then begin
    FLayer.FEngine.FDispList.Add(TREDisplayRec.Create(FLayer, FLayer.FCurStep));
    if FEngine.FDispList.Count = 1 then//подавляем накопление лишних нотификаций в лаге до захвата данных основным потоком
        PostMessage(FLayer.FEngine.FWND, WM_RE_NOTIFY, 0, 0);
  end;
  LeaveCriticalSection(FRCS);


 
Ega23 ©   (2010-12-21 13:16) [51]


> А так можно (см. отмеченные строчки)?


Дурость.

Вот смотри:

procedure TForm1.Button1Click(Sender: TObject);
begin
 try
   if CheckBox1.Checked then Exit;
   ShowMessage("try block");
 finally
   ShowMessage("finally block");
 end;
end;


 
han_malign   (2010-12-21 13:57) [52]


>       EnterCriticalSection(FRCS);
>       for i := 0 to FDispList.Count - 1 do
>         if i < FDispList.Count then
>           FOnStep(Self,
>                   FDispList.DisplayRec[i].FLayer,
>                   FDispList.DisplayRec[i].FCurStep); <<<<<<<<<<<<<<<
>       FDispList.Clear;
>       LeaveCriticalSection(FRCS);

- поясняю схематически(каждый раз создавать/удалять объект моветон, но углубляться в кольцевые буфера мне что то лень):
TREDisplayRec.FLayer - должно быть копией мгновенного состояния, а не ссылкой на рабочий объект текущего состояния!!!
(иначе, вообще говоря, смысла в списке никакого нет)

      EnterCriticalSection(FRCS);
      snapshotDispList:= FDispList;
      FDispList:= T
КакойТоТамDispList.Create;
      LeaveCriticalSection(FRCS);//позволяю работать остальным потокам!!!
      for i := 0 to snapshotDispList.Count - 1 do
        //if i < FDispList.Count then - бред
          FOnStep(Self,
                  snapshotDispList.DisplayRec[i].FLayer,
                  snapshotDispList.DisplayRec[i].FCurStep);
      snapshotDispList.Free;

sapienti sat


 
abb777 ©   (2010-12-21 20:05) [53]

To han_malign:

............
> FDispList:= TКакойТоТамDispList.Create;
...........

Я этого всего совсем не понял. Извините. У меня объект списка вообще создавался 1 раз. А Вы предлагаете его:
а) создавать в ОСНОВНОМ треде, а не там, где непосредственно появляются данные для него. Кто будет данные туда пихать и зачем, если можно просто опросить все TRLayer-ы об их состоянии?
б) создавать каждый раз при вызове процедуры отображения, а не 1 раз в самом начале.


> //подавляем накопление лишних нотификаций в лаге до захвата
> данных основным потоком


Идея была такая. Как Вы можете видеть, в каждом TRLayer.Execute фигурирует TRLayer.FCurStep, определяющая текущий шаг расчета. Эта переменная устанавливается одновременно со сбросом состояния FIsReady. Затем после опр. вычислений FIsReady устанавливается, что говорит о том, что данный объект (тред) закончил опр. этап расчетов на шаге FCurStep, и информация для его соседа готова. Сосед может начать шаг расчета. Сам же тред идет дальше и еще кое-чем занят какое-то время. И только после этого он сообщает треду отображения о том, что инфа по очередному шагу FCurStep готова и увеличивает FCurStep на 1 переходя к следующему шагу. Поэтому тред отображения должен иметь не только ссылку на TRLayer[i], откуда он берет всю инфу (которой много), но и значение FCurStep, для которого TRLayer[i] это все сосчитал, т.к. в момент работы треда отображения у рабочего треда FCurStep может быть уже другой, а состояние инфы для этого шага еще не подтверждено. Поэтому опрерируем указателем на TRLayer[i] и значением TRLayer[i].FCurStep, которые были переданы. Я не пойму, в чем изъян такого метода?

А если мы "подавим лишние нотификации", то инфа будет приходить отрывочными кусками по каким-то случайным (а не по всем) TRLayer-ам. Разве нет? Ведь список наполняется не одним TRLayer-ом, а разными и независимо друг от друга. И надо учесть, что процедура расчета в каждом "рабочем" треде будет работать на порядки (на много порядков) дольше, чем все эти создания объектов, помещения их в список и т.п. Там будет решаться система из 7 нелин. дифуров, а это довольно трудоемкий процесс, насколько я знаю...

To Ega23 :
Да, что-то я сам себя перехитрил. Решил стать святее папы римского :)

TO ALL:
Всем огромное спасибо за поддержку, только скажите все же, я правильно понимаю, что треды мои висели скорее всего на каком-то входе в крит. секцию, потому что из нее не было сделано выхода вследствие необработки какого-то исключения?


 
abb777 ©   (2010-12-21 20:25) [54]


>       EnterCriticalSection(FRCS);
>       snapshotDispList:= FDispList;
>       FDispList:= TКакойТоТамDispList.Create;
>       LeaveCriticalSection(FRCS);//позволяю работать остальным
> потокам!!!
>

Кажется, я понял Вашу мысль. Спасибо.


 
abb777 ©   (2010-12-21 21:15) [55]

To Ega23:

> Дурость.
>
> Вот смотри:
>
> procedure TForm1.Button1Click(Sender: TObject);
> begin
>  try
>    if CheckBox1.Checked then Exit;
>    ShowMessage("try block");
>  finally
>    ShowMessage("finally block");
>  end;
> end;


При ближайшем рассмотрении нашел неувзку. Вы показываете мне, что finally сработает даже при вызове Exit(), так? А я с этим и не спорил. Но в моем коде внутри try стоял LeaveCriticalSection(). Его два раза подряд можно вызывать? Я думал, что это неправильно. А если в finally не вставить проверку, то LeaveCriticalSection вызовется два раза.


 
Leonid Troyanovsky ©   (2010-12-21 22:06) [56]


> abb777 ©   (21.12.10 20:05) [53]

Читать Джефа Рихтера.
До просветления.

--
Regards, LVT.


 
Дмитрий Белькевич   (2010-12-22 00:03) [57]


> Ну может я и балбес, но я вижу где отладчик ходит. А в данном
> случае он ходит именно внутри WaitFor(


Не важно, где ходит отладчик. Важно, что waitfor не может дождаться конца метода execute треда. А пока execute не закончились, треды будут живыми. И кто знает, что они там наворотят после того, как половина программы будет убита. Вызов terminate тред не остановит - это только флаг.


 
Дмитрий Белькевич   (2010-12-22 00:15) [58]

>Внутри Execute() все с виду ОК - Terminated там анализируется

Тред никогда не выходит из Execute (читай - не заканчивается), если виснет на waitfor. Можешь флаг состояния какой-нибудь тестовый дописать или лог и проанализировать, когда треды начинаются, когда заканчивается. Если в реалтайме не удаётся отловить всё, хотя должно.
Мы у себя бывает по десятку разнородных тредов и по 50 одновременных ковыряем - то без логов повесились бы.


 
Ega23 ©   (2010-12-22 00:21) [59]


> При ближайшем рассмотрении нашел неувзку. Вы показываете
> мне, что finally сработает даже при вызове Exit(), так?
> А я с этим и не спорил. Но в моем коде внутри try стоял
> LeaveCriticalSection(). Его два раза подряд можно вызывать?
>  Я думал, что это неправильно. А если в finally не вставить
> проверку, то LeaveCriticalSection вызовется два раза.
>


Дело именно в этом.
Например, я захожу в крит.секцию. Получаю какой-то там параметр. На основании значения этого параметра мне надо либо продолжить работу, либо выйти.
Например, параметр - string

Вариант 1
var
 s: string;
b: Boolean;
begin
b := False;
EnterCritSection
try
 s := GetSomeParam;
 if (s = "мама") or (s = "мыла") or (s = "раму") then
 begin
   b := True;
   LeaveCritSection;
   Exit;
 end;
 s := DoSomethingWithParam(s);
 if (s = "мама") or (s = "мыла") or (s = "раму") then
 begin
   b := True;
   LeaveCritSection;
   Exit;
 end;
 ........ Ещё какой-то код
finally
 if not b then LeaveCritSection;
end;

Вариант 2
Тот же код
var
 s: string;
begin
EnterCritSection
try
 s := GetSomeParam;
 if (s = "мама") or (s = "мыла") or (s = "раму") then
   Exit;
 s := DoSomethingWithParam(s);
 if (s = "мама") or (s = "мыла") or (s = "раму") then
   Exit;
 ........ Ещё какой-то код
finally
 LeaveCritSection;
end;


Оба приведённых кода с точки зрения логики равнозначны. Но второй - как минимум тупо меньше писать надо. А как максимум - нет ненужных проверок (которые, теоретически, может выкинуть оптимизатор, но далеко не факт, что выкинет).
Собственно, всё.


 
han_malign   (2010-12-22 10:43) [60]


> А если мы "подавим лишние нотификации", то инфа будет приходить
> отрывочными кусками по каким-то случайным (а не по всем)
> TRLayer-ам. Разве нет?

- разве нет... для PostMessage, сообщение помещается в конец очереди сообщений.
Пока до него дойдет очередь:
Layers:  CritSect(Count:=1;notify),CritSect(Count:=2),...,CritSect(Count:=N)
Main(Message Dispatch Loop): CritSect((Count=N): Count:=0)
Layers:  CritSect((Count=0): Count:=1,notify)...


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

Конечно можно просто проверять, что есть новые данные(Count > 0), но в некоторых сильно-нагруженных сценариях можно запросто словить переполнение очереди сообщений...

> вариант с SendMessage() тоже работает, но вокруг него надо убрать крит. секцию.

- естественно(обычно циклограмму в столбцах рисуют по количеству потоков, но тут все равно линейная сериализация получается):
Layer Thread: EnterCriticalSection
   SendMessage/Synchronize { - Layer Thread - в ожидании завершения синхронной операции
Main Thread: EnterCriticalSection - опаньки!!!
Main Thread: LeaveCriticalSection
   }
Layer Thread: LeaveCriticalSection
классический DeadLock
--------------------------------
Layer Thread: EnterCriticalSection
   PostMessage()
Main Thread(Message Dispatch)  EnterCriticalSection(call) - WAIT ...
Layer Thread: LeaveCriticalSection
Main Thread(Message Dispatch):  ... EnterCriticalSection(return)
Main Thread(Message Dispatch): LeaveCriticalSection


- мораль: как можно меньше вложенных синхронизаций, как можно короче критические секции...
Для данного примера это можно было решить так:
EnterCriticalSection();
haveNewData:= Count > 0;//Layer Threads Owned Data
LeaveCriticalSection();
if( haveNewData )then Synchonize();
- хотя, в данном случае, для скаляра - можно спокойно использовать оптимистическую модель синхронизации - то есть вообще без критической секции...



Страницы: 1 2 вся ветка

Форум: "Основная";
Текущий архив: 2012.05.13;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.72 MB
Время: 0.005 c
2-1325831953
BeginMan
2012-01-06 10:39
2012.05.13
Как осуществить выборку по диапазонам дат.


2-1325889921
Gu
2012-01-07 02:45
2012.05.13
beep


3-1275468246
Miau
2010-06-02 12:44
2012.05.13
Кто подключен к серверу?


15-1325924494
Karabaz
2012-01-07 12:21
2012.05.13
TreeView многостолбцовый ищу


1-1292783101
abb777
2010-12-19 21:25
2012.05.13
TThread + Synchronize = bug





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский