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

Вниз

Как разрулить работу потока в вызовах его методов?   Найти похожие ветки 

 
Aleksandr.   (2005-02-11 15:12) [0]

Есть класс-наследник от TThread, имеющий свойство FList TList, в котором содержатся объекты с информацией для выполнения работы, метод AddQuery для добавления этих объектов в List, и метод,который работает внутри Execute:

type
 TQueryThread = class (TThread)
 private
    FList   : TList;
    FListCS : TCriticalSection;
    procedure DoQuery(aQuery : TExecQuery);
 public
    procedure AddQuery(aQuery : TExecQuery);
    procedure Execute; override;
    ...
 end;
   
procedure TQueryThread.AddQuery(aQuery : TExecQuery);
begin
 FListCS.Acquire;
 try
   FList.Add(aQuery)
 finally
   FListCS.Release
 end
end;

procedure TQueryThread.Execute;
var
 Q : TExecQuery;
begin
 repeat
   while GetQueryCount>0 do begin     // защищенный FListCS FList.Count
     FListCS.Acquire;
     try
       Q:=TExecQuery(FList.Items[0]);
       FList.Delete(0)
     finally
       FListCS.Release
     end;
     DoQuery(Q)
   end;
   Suspend
 until Terminated
end;

Код DoQuery приводить не буду, он очень большой и длительный по исполнению.
Есть также менеджер потоков TQueryThread, который, получая список из TExecQuery, раздает его по потокам:

procedure TQueryManager.ProcessQueries(L : TList);
var
 i, j : integer;
begin
 for i:=0 to L.Count-1 do begin
   for j:=0 to FQueryThreadList.Count-1 do
     if FQueryThreadList.Items[j].ID=TExecQuery(L.Items[i]).ID then begin
       FQueryThreadList.Items[j].AddQuery(TExecQuery(L.Items[i]));
       if FQueryThreadList.Items[j].Suspended then
         FQueryThreadList.Items[j].Resume;
       Break
     end
 end
end;


Вот вся эта схема время от времени дает длительные затыки. Лог выполнения показывает, что происходит примерно следующее:
Добавляется объект запроса в нить, она входит в метод DoQuery,
в это время добавляется еще один объект, и нить прекращает выполнять DoQuery. И обрабатывает все полученные объекты только тогда, когда перестанет вызываться AddQuery, возникнет пауза, а затем добавится еще один объект, добавление которого вызовет резюмирование нити. Может ли такое быть и как это можно разрулить? Догадываюсь, что есть вариант с блокировкой критической секцией между методами AddQuery и DoQuery, но он вызывает у меня сомнения, т.к. теряется смысл в FList, а метод DoQuery очень длительный.


 
Digitman ©   (2005-02-11 15:23) [1]

первое же что коробит глаз - между GetQueryCount и FListCS.Acquire состояние списка FListCS может измениться


 
Aleksandr.   (2005-02-11 15:35) [2]

Digitman ©  :
То есть это все должно быть в критической секции? Но это же не критично - обращение идет к первому элементу FList а не последнему? Хотя на 2-процессорной машине, может, и имеет...


 
Digitman ©   (2005-02-11 15:47) [3]

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


 
Aleksandr.   (2005-02-11 15:54) [4]

Digitman ©  :
Не понял... Если Вы о FList, то он же является свойством нити и используется только в ее методах? Кем же он еще используется?


 
Aleksandr.   (2005-02-11 18:10) [5]

Истчо вопрос на эту тему рождается: если нить находится в процессе выполнения Execute, а ей кто-нибудь вызывает resume - что произойдет с текущего выполняющегося метода? Он просто сбросится и нить вернется в начало цикла Execute?


 
Eraser ©   (2005-02-11 19:42) [6]

Aleksandr
если нить находится в процессе выполнения Execute, а ей кто-нибудь вызывает resume - что произойдет с текущего выполняющегося метода?


Этот метод продолжет выполнение...


 
Leonid Troyanovsky ©   (2005-02-11 20:54) [7]


> Aleksandr.   (11.02.05 15:12)  
> Есть класс-наследник от TThread, имеющий свойство FList
> TList, в котором содержатся объекты с информацией для


TThreadList.

--
Regards, LVT.


 
Alexander Panov ©   (2005-02-11 21:51) [8]

Как часто у тебя вызывается
procedure TQueryManager.ProcessQueries(L : TList);
?


 
Aleksandr.   (2005-02-11 23:17) [9]

Leonid Troyanovsky ©  :
Я, если честно, каких-то преимуществ TThreadList"a перед просто List"ом не увидел. Потому и не стал изгаляться.

Alexander Panov © :
Достаточно часто. В пиковые часы загрузки может и каждые 10 секунд вызываться.


 
Leonid Troyanovsky ©   (2005-02-12 01:11) [10]


> Aleksandr.   (11.02.05 23:17) [9]

> Я, если честно, каких-то преимуществ TThreadList"a перед
> просто List"ом не увидел. Потому и не стал изгаляться.


TThreadList защищает свой список критической секцией.
Так что, более ничего и не требуется.

--
Regards, LVT.


 
Aleksandr.   (2005-02-12 02:17) [11]

Leonid Troyanovsky © :
Ну, все равно какое-то разруливание требуется, наверное. Пусть я заменю код на

procedure TQueryThread.AddQuery(aQuery : TExecQuery);
begin
  FList.LockList.Add(aQuery);
  FList.UnlockList
end;

procedure TQueryThread.Execute;
var
Q : TExecQuery;
begin
repeat
  while
    try
      Q:=TExecQuery(FList.LockList.Items[0]);
      FList.UnLockList;
      FList.LockList.Delete(0);
      FList.UnLockList
    DoQuery(Q)
  end;
  Suspend
until Terminated
end;

Что это принципиально изменит?


 
Aleksandr.   (2005-02-12 02:18) [12]

Млин...


 
Leonid Troyanovsky ©   (2005-02-12 03:29) [13]

> Aleksandr.   (12.02.05 02:17) [11]

> procedure TQueryThread.AddQuery(aQuery : TExecQuery);
> begin
>   FList.LockList.Add(aQuery);
>   FList.UnlockList
> end;

Просто FList.Add(aQuery).
И

procedure TQueryThread.Execute;
var
Q : TExecQuery;
begin
   while not Terminated do
     begin
       with FList.Lock do
         try
           if Count = 0 then
             Break; // завершаемся или выход на ожидание(?)
           Q:=TExecQuery(Items[0]);
           Delete(0);
         finally
           FList.UnLockList;
         end;
       DoQuery(Q); // внешние try опущены
       Q.Free; // Возможно, что требует Synchronize(..)
       // Suspend;
    end;
end;

И что за Suspend? Ecли нужна реакция на событие,
то WaitFor*Object, можно и без Terminated.

> Что это принципиально изменит?

А что принципиально?
Если нужна асинхронная обработка, то нужен пул потоков,
каждый из которых будет обрабатывать один TExecQuery.

--
Regards, LVT.


 
Aleksandr.   (2005-02-12 03:39) [14]

Leonid Troyanovsky © :
Спасибо за разъяснения! Только я со Suspend не понял. Мне не надо завершаться, мне надо переходить в ожидание, когда будет добавлен следующий запрос. Вы имеете в виду, что надо в констракторе создать эвент, а по выполнении DoQuery переходить в инфинитный WaitForSingleObject установки этого эвента?
А что до Q.Free, так этого вообще тут не надо, как я понимаю - объект создается в другом потоке, содержит в себе событие, которое надо установить по окончании DoQuery, и в нем же уничтожится, этого события (точнее, их комплекта от всех объектов переданного менеджеру в ProcessQueries списка) поток-создатель ожидает в WaitForMultipleObjects, за которым дестракторы и вызовет.


 
Leonid Troyanovsky ©   (2005-02-12 04:43) [15]


> Aleksandr.   (12.02.05 03:39) [14]
> Спасибо за разъяснения! Только я со Suspend не понял. Мне
> не надо завершаться, мне надо переходить в ожидание, когда
> будет добавлен следующий запрос. Вы имеете в виду, что надо
> в констракторе создать эвент, а по выполнении DoQuery переходить
> в инфинитный WaitForSingleObject установки этого эвента?


Например, делаем EventDoit с ручным сбросом и

procedure TQueryThread.Execute;
..
begin
  while WaitForSingleObject(EventDoit, INFINITE) = WAIT_OBJECT_0 do
    begin
      if Terminated then
        Break;
      Q := nil;
      with FList.Lock do
        try
          if Count = 0 then
            ResetEvent(EventDoit)
          else
            begin
              Q:=TExecQuery(Items[0]);
              Delete(0);
            end;
        finally
          FList.UnLockList;
        end;
      if Assigned(Q) then
        DoQuery(Q);      
    end;
end;

Тогда, при добавлении в список нужно делать SetEvent.
Также SetEvent в Terminate:

SetEvent(EventDoit);
inherited;

и

destructor TQueryThread.Destroy; // override;
begin
  Terminate; // т.к. он не виртуальный
  inherited;
end;
 

> А что до Q.Free, так этого вообще тут не надо, как я понимаю
> - объект создается в другом потоке, содержит в себе событие,
> которое надо установить по окончании DoQuery, и в нем же
> уничтожится, этого события (точнее, их комплекта от всех
> объектов переданного менеджеру в ProcessQueries списка)
> поток-создатель ожидает в WaitForMultipleObjects, за которым
> дестракторы и вызовет.


Проще всего, когда эти объекты созданы в первичном (VCL)
потоке.
Тогда уничтожать их можно передавая указатель в Post(Thread)
Message окну или в Application.OnMessage.
Т.е., специфические ожидания и не потребуются.

--
Regards, LVT.


 
Alex Konshin ©   (2005-02-12 05:29) [16]

Leonid Troyanovsky ©   (12.02.05 04:43) [15]
Неверно.

После выхода из WaitForSingleObject мы должны обработать все запросы, находящиеся в очереди и только после этого опять уйти на ожидание. В противном случае при почти одновременном получении более одного запроса будет исполнятся только первый, а остальные так и останутся в очереди.


 
Alex Konshin ©   (2005-02-12 05:49) [17]

Хотя да... У тебя вместо простой проверки в цикле наличия запросов,
делается замысловатые пассы с event с ручным сбросом.
Совершенно излишнее усложнение. Лишние обращения к системе - лишние накладные расходы, к тому и сложнее получается. Достаточно простого event с автоматом и двойной цикл while.
Еще вместо TList я бы предложил использовать связный список.


 
Leonid Troyanovsky ©   (2005-02-12 11:01) [18]


> Alex Konshin ©   (12.02.05 05:49) [17]


> Хотя да... У тебя вместо простой проверки в цикле наличия
> запросов,
> делается замысловатые пассы с event с ручным сбросом.
> Совершенно излишнее усложнение. Лишние обращения к системе
> - лишние накладные расходы, к тому и сложнее получается.

Просто такая схема обеспечит возможность добавления в список
после любого DoQuery. И реакцию на Terminate (не уверен, что
проверки Terminated внутри цикла достаточно для
мультипроцессорных систем).
Ведь, по условию, DoQuery длительная операция.

> Достаточно простого event с автоматом и двойной цикл while.
> Еще вместо TList я бы предложил использовать связный список.

Можно вообще без списка - хватит очереди APC потока.
Но, видимо, в оптимизации здесь нуждается тот самый DoQuery.

--
Regards, LVT.


 
Alex Konshin ©   (2005-02-12 11:26) [19]

Вставлять в твоем случае можно в любой момент кроме try...finally.
Также, как и в моем случае.
Насчет APC - опять-таки ненужное усложнение.
Для организации списка в моем случае достаточно одного поля в TExecQuery и пару переменных-указателей на первый элемент и на последнее поле связи.
Просто не понимаю, зачем извращаться, когда все прямолинейно и ясно? Тем более, что это и эффективнее. Не, можно, конечно, и очередь Windows задействовать, и еще чего-нибудь, только зачем?


 
Leonid Troyanovsky ©   (2005-02-12 12:26) [20]


> Alex Konshin ©   (12.02.05 11:26) [19]


> Вставлять в твоем случае можно в любой момент кроме try...finally.
> Также, как и в моем случае.

Да, конечно, со вставкой был не прав.
Еще не проснулся, sorry.

> Насчет APC - опять-таки ненужное усложнение.

APC один из самых эффективных механизмов.
Широко исползуется в системе- для передачи одного параметра -
прямо, для большего - вместе с memory mapped file (секцией).
Да и зачем городить свою очередь, когда есть готовая.
И выглядит все очень понятно: посылаем запрос рабочему потоку
- выполнить DoQuery, DoQuit и т.д.
А рабочий поток просто сидит в SleepEx(INFINITE, True).
Ну, конечно, для потоков-объектов нужно сделать threadvar с
ссылкой на себя, или передавать эту ссылку в TExecQuery.

> Просто не понимаю, зачем извращаться, когда все прямолинейно
> и ясно? Тем более, что это и эффективнее. Не, можно, конечно,
> и очередь Windows задействовать, и еще чего-нибудь, только
> зачем?

В данном случае можно как угодно, бо главная проблема лишь в
длительности DoQuery. Т.е., чего толку от замечательности
очереди, если она постоянно непуста.

--
Regards, LVT.


 
Набережных С. ©   (2005-02-12 14:04) [21]

>Leonid Troyanovsky ©   (12.02.05 12:26) [20]
>APC один из самых эффективных механизмов.

Имхо, эффективность у него точно такая же, как и у очередей сообщений, а вот работать с ним менее удобнее.

>Alex Konshin ©   (12.02.05 11:26) [19]
>Не, можно, конечно, и очередь Windows задействовать, и еще чего-нибудь, только зачем?

Использование очереди сообщений, как и APC, позволит отказаться и от TList, и от критических секций, и от эвентов.


 
Alexander Panov ©   (2005-02-12 14:57) [22]

Aleksandr.   (11.02.05 15:12)

Может быть, натолкнет на какие-либо мысли вот эта реализация твоей задачи:
http://home.ural.ru/~panov/projects/threadspool/threadspool.html


 
Eraser ©   (2005-02-12 15:18) [23]

Aleksandr.

Также в коллекции JEDI компонентов, есть целая вкладка для работы с потоками, по-моему то что надо.


 
Verg ©   (2005-02-12 15:41) [24]

Я бы сделал объект очередь. У нее public полем event с ручным сбросом.
Ее методы add, extractTop, getcount защищены критической секцией.

add
<Вход в КС>
добавляет новый элемент в конец очереди и взводит event (setEvent)
<Выход из КС>

extractTop
<Вход в КС>
Извлекает указатель на верхний элемент (Result:=), удаляет этот элемент и если очередь пуста, сбрасывает event (ResetEvent).
<Выход из КС>

Появления элементов в очереди ожидаем WaitFor... ф-циями

Queue : TQueue;

 while WaitForSingleObject(Queue.Event, INFINITE) = WAIT_OBJECT_0 do
 begin
   Query := Queue.extractTop();
   DoQuery(Query);
 end;


 
Aleksandr.   (2005-02-14 12:34) [25]

Ох... спасибо всем... Теперь буду пытаться понять все это... Особенно про ResetEvent. Я уже не первый год юзаю систему CreateEvent-SetEvent-CloseHandle, но впервые узнаю, что Event надо еще и сбрасывать. Что происходит в данном коде, если (не) вызывается ResetEvent?


 
Leonid Troyanovsky ©   (2005-02-14 22:03) [26]


> Aleksandr.   (14.02.05 12:34) [25]
> Ох... спасибо всем... Теперь буду пытаться понять все это...
> Особенно про ResetEvent. Я уже не первый год юзаю систему
> CreateEvent-SetEvent-CloseHandle, но впервые узнаю, что
> Event надо еще и сбрасывать. Что происходит в данном коде,
> если (не) вызывается ResetEvent?


Чаще используется CreateEvent with bManualReset=False.
А в этом случае событие сбрасывается автоматически тогда,
когда его дождался любой поток (который его WaitFor*).

В упомянутом коде ручной сброс предлагался для обеспечения
быстрого отклика на Terminate (or Free) потока.
Иначе, поток завершится лишь после исчерпания очереди
(я не очень уверен в действенности проверки Terminated = true
в многопроцессорных системах).

--
Regards, LVT.


 
Alex Konshin ©   (2005-02-14 22:11) [27]

(я не очень уверен в действенности проверки Terminated = true
в многопроцессорных системах).

А какая разница? Это всего лишь переменная.

Что происходит в данном коде, если (не) вызывается ResetEvent?
Я ж объяснил, что произойдет. Будет обрабатываться только первый запрос из очереди. Чтоб этого не происходило, можно организовать еще один цикл. Здесь же предлагаются фокусы с event с ручным сбросом и лишние системные вызовы. Мне такой подход не нравится. Что делать тебе - дело твое.


 
Leonid Troyanovsky ©   (2005-02-14 23:19) [28]


> Alex Konshin ©   (14.02.05 22:11) [27]
> (я не очень уверен в действенности проверки Terminated =
> true
> в многопроцессорных системах).
>
> А какая разница? Это всего лишь переменная.


А, действительно... Уговорил :)
Внешний - while & Event с автосбросом,
внутренний - while not Terminated.

--
Regards, LVT.


 
Aleksandr.   (2005-02-17 16:44) [29]

Ну ничего у меня не выходит, млин! Какие странные ступоры выплывают. Мало того, по условию теперь в свойства нити необходимо добавить еще один List для объектов другого типа, да еще и булевое свойство для третьего вида обработки. Я попытался сделать их как интерпретацию объектов первого вида:
в новом методе в FUpdList добавляются объекты другого типа, после чего создается TExecQuery с предопределенными свойствами и вызывается опять же AddQuery. Ну а в обработчике DoQuery в зависимости от свойств рассматриваемого TExecQuery вызывается тот или иной метод. Все это вроде бы и работает, но иногда все равно происходит нестыковка - менеджер добавляет запросы, а потоки их уже не обнаруживают. Удалось только снизить вероятность такого затыка защитой одной критической секцией методов AddQuery и всего содержимого участка while WaitForSingleObject...end, что опять же привело к неприятному эффекту - все потоки, размещающие через менеджер потоков свои запросы, вынуждены дожидаться разблокирования секций на период весьма не быстрой работы DoCurrentQuery. Код теперь выглядит так

В менеджере:
// этот метод добавляет нитям в очередь запросы
procedure TQueryManager.ProcessNewQuery(Q: TExecQueryList);
var
 i : integer;
 A : TQueryThread;
 T : smallint;
begin
 FResetCS.Enter;
 try
   T:=-1;
   try
     for i:=0 to Q.QueryCount-1 do begin
       try
         T:=Q.Items[i].qID;
         A:=Threads[T];  // метод возвращает нужную нить
         if Assigned(A) then
           A.AddQuery(Q.Items[i])
         else begin
           DoLog(Format("Не удалось найти обработчик для %d, ручной сброс ожидания",[T]));
           Q.Items[i].qHandled:=true;
           SetEvent(Q.Items[i].qHandle)
         end
       except
         on E:Exception do begin
           DoLog(Format("Не удалось найти обработчик для %d "+E.Message,[T]));
           Q.Items[i].qHandled:=true;
           SetEvent(Q.Items[i].qHandle)
         end
       end
     end
   except
     on E:Exception do
       LogDBError(T,"Process new query: "+E.Message)
   end
 finally
   FResetCS.Leave
 end
end;

// этот метод добавляет нитям в очередь списки объектов для обработки другого типа
procedure TQueryManager.ProcessATIUpdate(IDT: word; EvList: TATIUpdateList);
var
 A : TQueryThread;
begin
FResetCS.Enter;
try
 if EvList.Count>0 then begin
   A:=Threads[IDT];
   if Assigned(A) then
     A.AddUpdates(EvList)
   else
     EvList.Clear
 end
finally
  FResetCS.Leave
end
end;

в нити:
// этот метод добавляет в свой список элемент запроса
procedure TQueryThread.AddQuery(Query: TExecQuery);
begin
 DoLog(Format("Запрос добавлен для %d",[Query.qUserID]));
 FExecCS.Acquire;
 try
   DoLog("Добавление запроса");
   FQueryList.Add(Query);
   SetEvent(FExecEvent)
 finally
   FExecCS.Release
 end
end;

// этот метод добавляет в список объектов пакет объектов для одного использования
procedure TQueryThread.AddUpdates(EvList: TATIUpdateList);
var
 Q : TExecQuery;
begin
 if NOT ((NOT Assigned(EvList)) OR (EvList.Count=0) OR NOT Assigned(FUpdateList)) then begin
   FUpdateCS.Enter;
   try
     while EvList.Count>0 do begin
       FUpdateList.Add(EvList.Items[0].GetCopy);
       EvList.Delete(0)
     end
   finally
     FUpdateCS.Leave
   end;
   Q:=TExecQuery.Create(0,qiOtherInfo,0,0);
   AddQuery(Q)
 end
end;

procedure TQueryThread.Execute;
var
 Q     : TExecQuery;
begin
 try
   Coinitialize(nil);
   try
     FSQLUpdator:=TATISQLUpdator.Create(FIDT);
     try
       LoadCashedData;
       CalculateMemBuffers;
       CalCulateIndexes;
       while WaitForSingleObject(FExecEvent,INFINITE)=WAIT_OBJECT_0 do try
         if Terminated then
           Break;
         FExecCS.Acquire;
         try
           CheckFileIndex;
           with FQueryList.LockList do try
             while Count>0 do begin
               Q:=Items[0];
               if Q.qType=qiOtherInfo then begin
                   case Q.qID of
                     0 : ProcessUpdates;
                     1 : ProcessClearing;
                   end;
                   Q.Free                     // убиваем запрос, потому как создан самой нитью
               end
               else
                 DoQuery(Q);           // иначе запрос будет убит потоком, который его создал и передал менеджеру
               Delete(0)
             end
           finally
             FQueryList.UnlockList
           end;
//            ReSetEvent(FExecEvent)          // вроде как не надо, потому что создан   =CreateEvent(nil, false, False, PChar(IntToStr(Self.ThreadID)+"thread"));

         finally
           FExecCS.Release
         end
       except
         on E:Exception do
           DoLog("Ошибка цикла, "+E.Message)
       end
     finally
       FreeAndNil(FSQLUpdator)
     end
   finally
     CoUninitialize
   end
 except
   on E:Exception do
     DoLog("Ошибка Execute "+E.Message)
 end
end;



 
Набережных С. ©   (2005-02-18 08:13) [30]

А мож примерно так, а?

const
 SN_QUERY = WM_USER + 400;
 SN_QUIT = SN_QUERY + 1;

type
 TExecQuery = class
 // Stub
 end;

 TQueryThread = class(TThread)
 private
   procedure DoQuery(aQuery : TExecQuery);
 protected
   procedure Execute; override;
public
   procedure AddQuery(aQuery : TExecQuery);
   procedure Terminate;
 end;

implementation

{ TQueryThread }

procedure TQueryThread.AddQuery(aQuery: TExecQuery);
begin
 PostThreadMessage(ThreadID, SN_QUERY, integer(aQuery), 0);
end;

procedure TQueryThread.DoQuery(aQuery: TExecQuery);
begin
// Stub
end;

procedure TQueryThread.Execute;
var
 Msg: TMsg;
begin
 while GetMessage(Msg, 0, 0, 0) do
 begin
   case Msg.message of
     SN_QUERY: DoQuery(TExecQuery(Msg.wParam));
     SN_QUIT:
     begin
       while PeekMessage(Msg, 0, SN_QUERY, SN_QUERY, PM_REMOVE) do
         TExecQuery(Msg.wParam).Free;
       Break;
     end;
   end;
 end;
end;

procedure TQueryThread.Terminate;
begin
 PostThreadMessage(ThreadID, SN_QUIT, 0, 0);
 inherited;
end;


 
Digitman ©   (2005-02-18 09:08) [31]

я бы даже так вот так сделал это :

TQueryThread = class(TThread)
private
  procedure DoQuery(aQuery : TExecQuery);
protected
  procedure Execute; override;
public
 destructor Destroy; override;
 procedure AddQuery(aQuery : TExecQuery);
end;
..

destructor TQueryThread.Destroy;
begin
if GetCurrentThreadId <> MainThreadId then
  PostThreadMessage(ThreadId, WM_QUIT, 0, 0);
inherited;
end;

procedure TQueryThread.AddQuery(aQuery: TExecQuery);
begin
PostThreadMessage(ThreadID, SN_QUERY, integer(aQuery), 0);
end;

procedure TQueryThread.DoQuery(aQuery: TExecQuery);
begin
// Stub
end;

procedure TQueryThread.Execute;
var
Msg: TMsg;
begin
try
try
 while not Terminated and GetMessage(Msg, 0, 0, 0) do
  Dispatch(Msg.Message);
finally
  while PeekMessage(Msg, INVALID_HANDLE_VALUE, SN_QUERY, SN_QUERY, PM_REMOVE) do
    try
     TExecQuery(Msg.wParam).Free;
    except
    end;
end;
except
end;
end;


 
Digitman ©   (2005-02-18 09:12) [32]

да, забыл  ..

TQueryThread = class(TThread)
private
 procedure MsgQuery(var Message: TMessage); message SN_QUERY;
 procedure DoQuery(aQuery : TExecQuery);
protected
 procedure Execute; override;
public
destructor Destroy; override;
procedure AddQuery(aQuery : TExecQuery);
end;
..

procedure TQueryThread.MsgQuery(var Message: TMessage);
var
 ExecQuery: TExecQuery;
begin
ExecQuery := TExecQuery(Message.wParam);
try
 DoQuery(ExecQuery);
finally
  ExecQuery.Free;
end;
end;


 
Набережных С. ©   (2005-02-18 14:48) [33]


> Digitman ©   (18.02.05 09:08) [31]

Конечно нужно делать так, как у тебя:) Мой пример и кодом-то назвать нельзя:) Эскиз, набросанный за 5 минут, в котором полно ошибок, как логических, так и технических



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

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

Наверх




Память: 0.59 MB
Время: 0.042 c
14-1108420482
Knight
2005-02-15 01:34
2005.03.06
Кто в какой последовательности бореться с BSOD?


1-1108834357
NightStranger
2005-02-19 20:32
2005.03.06
Сохранение HTML


4-1106240656
Arnold
2005-01-20 20:04
2005.03.06
Проблема с хуком WH_MOUSE


3-1107349942
Топпер
2005-02-02 16:12
2005.03.06
FastReport нумерация строк данных


6-1102991145
Timur
2004-12-14 05:25
2005.03.06
Счетчик трафика





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский