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

Вниз

Пул потоков.   Найти похожие ветки 

 
panov ©   (2004-10-14 14:34) [0]

Создал класс для работы с пулом потоков.
Прошу высказать замечания.
В следующих постингах задам вопросы к коду.


unit uTThreadPool;

interface

uses
 Classes, windows, messages;

var
 WM_EndThreadExecuter: DWORD;      //Сообщение об окончании обработки
 WM_AddThread: DWORD;              //Сообщение о добавлении элемента в очередь

type
//Тип процедуры для выполнения в отдельном потоке
 TThreadProc=procedure(aParm: pointer);

//Структура для передачи параметров
 PQueueRec=^TQueueRec;
 TQueueRec=record
   rProc: TThreadProc;
   rParm: Pointer;
 end;

//Элемент пула потоков
 TThreadExecuter=class(TThread)
 private
   FThreadIdOwner: THandle;        //Родительский поток TThreadPool
   FThreadFree: Boolean;           //Поток свободен/занят работой
   FExecProc: TThreadProc;         //Адрес процуедуры для выполнения
   FParm: Pointer;                 //Указатель на параметр,
                                   //Предаваемый в процедуру FExecProc
   FNumThread: Integer;            //Нмер потока в пуле
 protected
   procedure Execute; override;
 public
//В конструктор передается ThreadId родительского потока TThreadPool
   constructor Create(const aOwnerThread: THandle;const aNumThread: Integer);
 end;

//Менеджер пула потоков TThreadExecuter
 TThreadPool = class(TThread)
 private
{ TODO -cВажно :
Добавить код для проверки работоспособности потоков из пула
Зависшие убивать по таймауту }
   FSemaphore: THandle;            //Семафор-счетчик потоков
   FMaxThreads: Integer;           //Максимальное число потоков в пуле
   FListQueue: TList;              //Очередь заданий на выполнение
   FPool: array of TThreadExecuter; //Пул потоков
   procedure JobAdd(const aProc: TThreadProc;const aParm: Pointer); //Добавление
                                                                    //заданий в очередь
   function StartThreadFromQueue: Boolean; //Передача задания из очереди в пул
                                           //на выполнение
 protected
   procedure Execute; override;
 public
//В конструктор передается максимальное число потоков
   constructor Create(aMaxThreads: Integer);
   destructor Destroy;override;
//Добавление заданий из внешних потоков в очередь потоков
//Параметры:
//  aProc - адрес процедуры для выполнения
//  aParm - адрес памяти, по которуму нахлодятся параметры
//          для выполнения прцедуры
   procedure AddThread(aProc:TThreadProc;aParm:Pointer);
 end;

implementation

uses Unit1;

{ TThreadExecuter }

constructor TThreadExecuter.Create(const aOwnerThread: THandle;const aNumThread: Integer);
begin
 inherited Create(True);
 FreeOnTerminate := True;
 FThreadFree := True;
 FThreadIdOwner := aOwnerThread;
 FNumThread := aNumThread;
 Resume;
end;

procedure TThreadExecuter.Execute;
begin
 while not Terminated do
 begin
   Suspend;                        //Засыпаем сразу
   try
     if Assigned(FExecProc) then FExecProc(FParm); //Выполнение переданной
                                                   //процедуры
   except
   end;
//Передача в родительский поток сообщения о завершении обработки
   PostThreadMessage(FThreadIdOwner,WM_EndThreadExecuter,FNumThread,0);
   if Terminated then break;
 end;
end;



 
panov ©   (2004-10-14 14:35) [1]


{ TThreadPool }

constructor TThreadPool.Create(aMaxThreads: Integer);
begin
 inherited Create(True);
//Непонятно пока, как сделать с наименьшими потерями возможность
//полностью инициализироваться потоку(успеть создать очередь
//сообщений), так как высокий приоритет не гарантирует этого
 Priority := tpTimeCritical;
 FMaxThreads := aMaxThreads;       //Максимальное число потоков в пуле
 FreeOnTerminate := True;
 FListQueue := TList.Create;       //Очередь заданий на выполнение
 FSemaphore := CreateSemaphore(nil,FMaxThreads,FMaxThreads,nil);
 Resume;
end;

destructor TThreadPool.Destroy;
var
 i: Integer;
begin
{ TODO -cВажно : Добавить обработку ошибок }
//Чистим очередь заданий
 for i := 0 to FListQueue.Count-1 do Dispose(PQueueRec(FListQueue[0]));
 FListQueue.Free;
//Очистка пула потоков
 for i := 0 to Length(FPool)-1 do FPool[i].Terminate;
 CloseHandle(FSemaphore);
 inherited;
end;

procedure TThreadPool.Execute;
var
 Msg: TMsg;                        //Приемник сообщений
 p: Pointer;                       //Фиктивный указатель
 RC: DWORD;                        //Код возврата
begin
{ TODO -cВажно : Защитить код try..finally..end }
 PeekMessage(Msg,0,0,0,pm_NoRemove); //Создание очереди сообщений
//Возвращаем нормальный приоритет - очередь сообщений создана
 Priority := tpNormal;
 p := nil;
 while not Terminated do
 begin
//Ждем появления любого сообщения в очереди сообщений
   rc := MsgWaitForMultipleObjects(0,p,False,10,QS_ALLPOSTMESSAGE);
{ TODO -cВажно : Обработать ошибку }
   if rc=WAIT_FAILED then
   begin
     Terminate;
     Continue;
   end;

   if rc= WAIT_TIMEOUT then
   begin
     if FListQueue.Count>0 then    //В очереди есть задания
     begin
       while StartThreadFromQueue do; //Передать в пул на выполнение
     end;
   end;

   if (rc=WAIT_OBJECT_0) or (PeekMessage(Msg,0,0,0,pm_NoRemove)) then
   begin
     GetMessage(Msg,0,0,0);
     if Msg.message=WM_QUIT then
     begin
       Terminate;
       Continue;
     end;

     if Msg.message=WM_EndThreadExecuter then
     begin
       FPool[Msg.wParam].FThreadFree := True; //Закончилось выполнение
                                              //задания в пуле
       ReleaseSemaphore(FSemaphore,1,nil);    //Увеличить счетчик свободных
                                              //потоков в пуле
       Continue;
     end;

     if Msg.message=WM_AddThread then
     begin
       JobAdd(Pointer(Msg.wParam),Pointer(Msg.lParam)); //Добавить задание в очередь
       while StartThreadFromQueue do; //Передать в пул на выполнение
       Continue;
     end;
   end;
 end;

end;

procedure TThreadPool.JobAdd(const aProc: TThreadProc;const aParm: Pointer);
var
 JobRec: PQueueRec;
begin
 New(JobRec);
 JobRec^.rProc := aProc;
 JobRec^.rParm := aParm;
 FListQueue.Add(JobRec);
end;

function TThreadPool.StartThreadFromQueue: Boolean;
var
   RC: Integer;
   i: Integer;
begin
 Result := False;
 if FListQueue.Count=0 then Exit; //В очереди нет заданий - выход
 RC := WaitForSingleObject(FSemaphore,0); //Есть свободные потоки в пуле?
 case RC of
   WAIT_OBJECT_0:              //Есть свободные потоки в пуле
     begin
       for i :=0 to Length(FPool)-1 do
       begin
         if FPool[i].FThreadFree then
         begin
           FPool[i].FThreadFree := False; //Флажок "не свободен"
           FPool[i].FExecProc := PQueueRec(FListQueue[0]).rProc;
           FPool[i].FParm := PQueueRec(FListQueue[0]).rParm;
           Dispose(PQueueRec(FListQueue[0]));
           FListQueue.Delete(0);
           Result := True;
           FPool[i].Resume;    //Разбудить поток для выполнения
           Exit;
         end;
       end;
       if Length(FPool)<FMaxThreads then //В пуле еще не максимальное
       begin                             //количество потоков?
         i := Length(FPool);
         SetLength(FPool,i+1);           //Добавляем поток в пул
         FPool[i] := TThreadExecuter.Create(Self.ThreadID,i);
       end;
     end;
   end;
end;

procedure TThreadPool.AddThread(aProc: TThreadProc; aParm: Pointer);
begin
//Пересылаем в очередь сообщений сообщение о новом задании
 PostThreadMessage(Self.ThreadID,WM_AddThread,Integer(@aProc),Integer(aParm));
end;

initialization
//Регистрируем сообщение об окончании обработки задания
 WM_EndThreadExecuter := RegisterWindowMessage("TThreadPoolWM_EndThreadExecuter");
//Регистрируем сообщение о появлении нового задания на обработку
 WM_AddThread := RegisterWindowMessage("TThreadPoolWM_AddThread");
finalization

end.


Вызов происходит следующим образом(Создавал для тестирования):

type
 PParmRec=^TParmRec;
 TParmRec=record
   n: Integer;
   TimeOut: integer;
 end;

procedure Exec1(aParm: pointer);
var
   Parm: PParmRec;
begin
 Parm := aParm;
 Sleep(Parm.TimeOut);
 Form1.lb.Items.Add("End "+IntToStr(Parm^.n)+"/"+IntToStr(Parm^.TimeOut));
 Dispose(Parm);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
   Parm: PParmRec;
   i: Integer;
begin
 Randomize;
 M := TThreadPool.Create(5);

 for i := 0 to 100 do
 begin
   New(Parm);
   Parm.n := i;
   Parm.TimeOut := (Random(10)+1)*30;
   M.AddThread(Exec1,Parm);
 end;
end;


 
Суслик ©   (2004-10-14 14:43) [2]

Замечание 1.


> constructor TThreadExecuter.Create(const aOwnerThread: THandle;const
> aNumThread: Integer);
> begin
>  inherited Create(True);
>  FreeOnTerminate := True;
>  FThreadFree := True;
>  FThreadIdOwner := aOwnerThread;
>  FNumThread := aNumThread;
>  Resume;
> end;


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

procedure TThread.AfterConstruction;
begin
 if not FCreateSuspended then
   Resume;
end;


Я бы написал просто так

constructor TThreadExecuter.Create(const aOwnerThread: THandle;const aNumThread: Integer);
begin
inherited Create(False);
FreeOnTerminate := True;
FThreadFree := True;
FThreadIdOwner := aOwnerThread;
FNumThread := aNumThread;
end;


 
panov ©   (2004-10-14 14:46) [3]

Вопрос:
После выполнения конструктора из TThreadPool проходит некоторое время, прежде чем будет создана очередь сообщений для потока.
После M := TThreadPool.Create(5) поток использовать нельзя до создания очереди сообщений.

Как лучше обработать эту ситуацию?
Сейчас временно в конструкторе присваивается высокий приоритет tpTimeCritical, а в методе Execute возвращается в исходное состояние.


 
Суслик ©   (2004-10-14 14:49) [4]

Замечание-вопрос 2
Почему в TThreadExecuter.Execute "глушится" exception? Не лучше ли бы было его как-то обработать?

Замечание 3.
Аналог первого замечания для TThreadPool.Create. Хотя, здесь эта маловероятна ошибка.

Замечание 4.
В destructor TThreadPool.Destroy почему пишешь

for i := 0 to FListQueue.Count-1 do Dispose(PQueueRec(FListQueue[0]));
а нет

if Assigned(FListQueue) then for i := 0 to FListQueue.Count-1 do Dispose(PQueueRec(FListQueue[0]));

?

пока все - дальше времени нет смотреть :)


 
panov ©   (2004-10-14 14:49) [5]

>Суслик ©   (14.10.04 14:43) [2]

constructor TThreadExecuter.Create(const aOwnerThread: THandle;const aNumThread: Integer);
begin
inherited Create(False);
FreeOnTerminate := True;
FThreadFree := True;
FThreadIdOwner := aOwnerThread;
FNumThread := aNumThread;
end


Так делать нельзя.

После inherited Create(False); немедленно стартует метод Execute, соответственно, остаются непроинициализированными переменные

FreeOnTerminate := True;
FThreadFree := True;
FThreadIdOwner := aOwnerThread;
FNumThread := aNumThread;


и метод Execute будет обращаться к еще неинициализированным переменным.


 
Суслик ©   (2004-10-14 14:53) [6]


> Вопрос:
> После выполнения конструктора из TThreadPool проходит некоторое
> время, прежде чем будет создана очередь сообщений для потока.
> После M := TThreadPool.Create(5) поток использовать нельзя
> до создания очереди сообщений.

Я бы сделал так.
1) Инстанцировал TThreadPool например, через метод класса TThreadPool. Например так

class function TThreadPool.Counsturct: TTHreadPool;
begin
   Result := TThreadPool.Create(...);
   SomeEvent.WaitFor;
end;

2) Событие SomeEvent устанавливал бы в signaled в TThreadPool.Execute после создания очереди потока.


 
panov ©   (2004-10-14 14:54) [7]

>Суслик ©   (14.10.04 14:49) [4]

Замечание-вопрос 2
Почему в TThreadExecuter.Execute "глушится" exception? Не лучше ли бы было его как-то обработать?

Замечание 3.


Скорее всего, обработка будет Exception будет добавлена.
К сожалению, для реализации стандартных потоков(TThread) в Delphi нельзя сделать Rerasing(хотя у меня есть сторонняя реализация от y-soft, которую я обязательно разберу).

Замечание 4.

Хорошее замечание, исправлю.


 
Суслик ©   (2004-10-14 14:54) [8]


>  [5] panov ©   (14.10.04 14:49)


> Так делать нельзя.

Ты не прав. Именно так и надо делать в дельфи 6. Не веришь? См. исходный код TThread.Create, AfterContruction и FCreateSuspended


 
Суслик ©   (2004-10-14 14:56) [9]


> К сожалению, для реализации стандартных потоков(TThread)
> в Delphi нельзя сделать Rerasing

Что значит нельзя?
Используешь AcquireExceptionObject и делаешь reraise как угодно.
См. исходный код classes для класса tthread.


 
panov ©   (2004-10-14 14:59) [10]

>Суслик ©   (14.10.04 14:54) [8]
Привожу код TThread.Create:

constructor TThread.Create(CreateSuspended: Boolean);
begin
 inherited Create;
 AddThread;
 FSuspended := CreateSuspended;
 FCreateSuspended := CreateSuspended;
 FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID);
 if FHandle = 0 then
   raise EThread.CreateResFmt(@SThreadCreateError, [SysErrorMessage(GetLastError)]);
end;


После выполнения выделенной строки выполнение поточной функции начнется немедленно.


 
Суслик ©   (2004-10-14 15:02) [11]


>  [10] panov ©   (14.10.04 14:59)


> После выполнения выделенной строки выполнение поточной функции
> начнется немедленно.

ну бог в помощь :)


> CREATE_SUSPENDED


а этааа что?


 
panov ©   (2004-10-14 15:07) [12]

>Суслик ©   (14.10.04 15:02) [11]

С AfterConstruction сейчас проверю


 
Суслик ©   (2004-10-14 15:09) [13]


>  [12] panov ©   (14.10.04 15:07)

из всего, что я сказал, скорее всего был не прав в этой фразе "Именно так и надо делать в дельфи 6." Тут надо еще подумать.

Но, то, что resume в коснтурторе делать нехорошо в Д6 это точно. Спасибо sha. Навел меня как-то на эту мысль.

---------------
Как решение обозначенной в вопросе проблемы с событием? Оно точно поможет, я всегда так делаю.


 
Суслик ©   (2004-10-14 15:14) [14]

Я бы сюда

    if Msg.message=WM_EndThreadExecuter then
    begin
      FPool[Msg.wParam].FThreadFree := True; //Закончилось выполнение
                                             //задания в пуле
      ReleaseSemaphore(FSemaphore,1,nil);    //Увеличить счетчик свободных
                                             //потоков в пуле
      Continue;
    end;


перед (или вместо) continue добавил бы вот этот кусочек

    if FListQueue.Count>0 then    //В очереди есть задания
    begin
      while StartThreadFromQueue do; //Передать в пул на выполнение
    end;


Это позволит не ждать 10 мс в случае если в очереди есть необработанные запросы.


 
panov ©   (2004-10-14 15:27) [15]

>Суслик ©   (14.10.04 15:14) [14]

Спасибо за совет, добавлю
while StartThreadFromQueue do;
вместо if FListQueue>0  while...


 
Суслик ©   (2004-10-14 15:28) [16]

Еще

destructor TThreadPool.Destroy;
var
  i: Integer;
begin
  ...
  //Очистка пула потоков
  for i := 0 to Length(FPool)-1 do FPool[i].Terminate;   ...
end;


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

Я бы лучше не делал потокам resume\suspend, а сажал их на ожидаение tevent


 
Суслик ©   (2004-10-14 15:31) [17]

Еще

в function TThreadPool.StartThreadFromQueue: Boolean;
в случае занятости делом всех потоков и недостижения макс. кол-ва потоков не верен будет результат: result будет равен false! Т.е. не все потоки будут запущены.


 
panov ©   (2004-10-14 15:33) [18]

Метод с inherited(False) не приводит к нужному результату.

Строка
 PostThreadMessage(Self.ThreadID,WM_AddThread,Integer(@aProc),Integer(aParm));
Выполняется раньше, чем код в Execute
PeekMessage(Msg,0,0,0,pm_NoRemove);

В результате выполнения

M := TThreadPool.Create(5);

for i := 0 to 100 do
begin
  New(Parm);
  Parm.n := i;
  Parm.TimeOut := (Random(10)+1)*30;
  M.AddThread(Exec1,Parm);
end;


т.е. обращение к методу AddThread начинается раньше, чем стартует метод Execute.


 
panov ©   (2004-10-14 15:35) [19]

>Суслик ©   (14.10.04 15:28) [16]
С терминированием - спасибо, упустил из вида.

 for i := 0 to Length(FPool)-1 do
 begin
   FPool[i].Terminate;
   FPool[i].Resume;
 end;


 
Суслик ©   (2004-10-14 15:37) [20]


>  [18] panov ©   (14.10.04 15:33)

Посмотри на ответ [6].
Там приведено общее решение: в главном потоке ждать TEvent, который будет установлен после создания очереди.


 
Суслик ©   (2004-10-14 15:39) [21]


> [20] Суслик ©   (14.10.04 15:37)

пояснения

var
  E: TEvent;

// Главный поток
pooler := TPool.Create();
E.WaitFor;

// Поток-элемент пулера
procedure TThreadExecutor.Execute;
begin
   Создание очереди
   E.SetEvent;
   ...
end;


Все.


 
Суслик ©   (2004-10-14 15:42) [22]


> [19] panov ©   (14.10.04 15:35)

Плохо.
А если поток уже делом занят?
Будет двойной вызов resume.

Лучше пользоваться event"ом.


 
panov ©   (2004-10-14 15:44) [23]

>Суслик ©   (14.10.04 15:39) [21]

Я понимаю, что можно использовать объекты ядра для ожидания.
Но нет ли более быстрого решения?
Создание и использование таких объектов занимает время и ресурсы.
Мне кажется, что для единовременного использования это неоправданно.

>Суслик ©   (14.10.04 15:31) [17]

Спасибо.
Действительно, не обрабатывался WAIT_TIMEOUT;
Изменил, теперь выглядит так:

function TThreadPool.StartThreadFromQueue: Boolean;
var
   RC: Integer;
   i: Integer;
begin
 Result := False;
 if FListQueue.Count=0 then Exit; //? ??????? ??? ??????? - ?????
 RC := WaitForSingleObject(FSemaphore,0);
 case RC of
   WAIT_OBJECT_0:              
     begin
       for i :=0 to Length(FPool)-1 do
       begin
         if FPool[i].FThreadFree then
         begin
           FPool[i].FThreadFree := False;
           FPool[i].FExecProc := PQueueRec(FListQueue[0]).rProc;
           FPool[i].FParm := PQueueRec(FListQueue[0]).rParm;
           Dispose(PQueueRec(FListQueue[0]));
           FListQueue.Delete(0);
           Result := True;
           FPool[i].Resume;    
           Exit;
         end;
       end;
       NewThread;
     end;
   WAIT_TIMEOUT:
     begin
       NewThread;
     end;
   end;
end;
procedure TThreadPool.NewThread;
var
 i: Integer;
begin
  if Length(FPool)<FMaxThreads then
  begin                            
    i := Length(FPool);
    SetLength(FPool,i+1);          
    FPool[i] := TThreadExecuter.Create(Self.ThreadID,i);
  end;
end;


 
panov ©   (2004-10-14 15:48) [24]

>Суслик ©   (14.10.04 15:42) [22]
А какая разница, сколько раз Resume вызывать?

По поводу [14] и [15] -
if FListQueue.Count>0 then    //В очереди есть задания
   begin
     while StartThreadFromQueue do; //Передать в пул на выполнение
   end;

Та же самая ситуация возникает, что и с TThreadPoool: поток не успевает начать выполнять метод TThreadExecuter.Execute


 
Суслик ©   (2004-10-14 15:55) [25]


>  [23] panov ©   (14.10.04 15:44)


> Действительно, не обрабатывался WAIT_TIMEOUT;

Я про это не говорил - пропустил.

Ошибка то осталась.
WAIT_OBJECT_0:      
 begin
      for i :=0 to Length(FPool)-1 do
      begin
        if FPool[i].FThreadFree then
        begin
          FPool[i].FThreadFree := False;
          FPool[i].FExecProc := PQueueRec(FListQueue[0]).rProc;
          FPool[i].FParm := PQueueRec(FListQueue[0]).rParm;
          Dispose(PQueueRec(FListQueue[0]));
          FListQueue.Delete(0);
          Result := True;
          FPool[i].Resume;    
          Exit;
        end;
      end;
      NewThread;
      здесь не задается Result в случае занятости всех потоков, НЕдостижения максимума потоков и наличия заданий, а должен т.к. иначе при определенных условиях не будет правильно отрабатывать while StartThreadFromQueue do;     end;


Про медленность объектов ядра:
извини уж, это полная как бы ерунда. Твой код в определенных случаях от потери производительности страдал (сейчас вроде исправил) много больше, чем он может пострадать от объектов ядра.

Все же, есть способы объходиться без ОЯ. Например см. класс TMultiReadExclusiveWriteSynchronizer. Там реализована очень скорострельная синхронизация БЕЗ объектов ядра. Если интересно смотри его.


 
Суслик ©   (2004-10-14 15:58) [26]


> Та же самая ситуация возникает, что и с TThreadPoool: поток
> не успевает начать выполнять метод TThreadExecuter.Execute

С чего это? TThreadExecuter уже создан. С чего ему не успевать что-то делать.


> А какая разница, сколько раз Resume вызывать?

Согласен, что в данном случае никакой.


 
panov ©   (2004-10-14 15:59) [27]

>Суслик ©   (14.10.04 15:55) [25]

Функция StartThreadFromQueue должна возвращать True в единственном случае - при передаче задания на выполнение.
В остальных случаях возвращается False.


 
panov ©   (2004-10-14 16:01) [28]

>Суслик ©   (14.10.04 15:58) [26]
С чего это? TThreadExecuter уже создан. С чего ему не успевать что-то делать.

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


 
Суслик ©   (2004-10-14 16:01) [29]


>  [27] panov ©   (14.10.04 15:59)


> Функция StartThreadFromQueue должна возвращать True в единственном
> случае - при передаче задания на выполнение.
> В остальных случаях возвращается False.

рассмотрел :)) Виноват:)


 
Суслик ©   (2004-10-14 16:03) [30]


>  [28] panov ©   (14.10.04 16:01)

предлагаю следующее.

1. Учесть те замечания в которых я оказался прав.
2. Реализовать синхронизацию в целях обеспечения своевременности создания очереди.
3. Выложить все в полном виде.

Тогда продолжим разговор. Ок?


 
Суслик ©   (2004-10-14 16:07) [31]

И вообще - на фига тут FSemaphore: он же используется в контексте одного потока?


 
Суслик ©   (2004-10-14 16:12) [32]


> [27] panov ©   (14.10.04 15:59)
> >Суслик ©   (14.10.04 15:55) [25]
>
> Функция StartThreadFromQueue должна возвращать True в единственном
> случае - при передаче задания на выполнение.
> В остальных случаях возвращается False.

Странная какая-то эта функция StartThreadFromQueue.
Ей говорят - запусти задание из очереди. Она - ок! Все потоки заняты, но макс. количества потоков не достигнут. Она создает новый поток и возврщает false. Тем самым говоря, клиенту (вызывающей стороне), то все, что могла она запустила. Странно как-то: поток есть (только что создан), задание есть, а вот выполнять его никто ПОКА не собирается - ждем следующего цикла.


 
panov ©   (2004-10-14 16:20) [33]

>Суслик ©   (14.10.04 16:03) [30]
2. Реализовать синхронизацию в целях обеспечения своевременности создания очереди.

Как раз это и хотелось обсудить.
Может быть, у кого еще будут предложения по синхронизации без использования объектов ядра, подобных Event

>Суслик ©   (14.10.04 16:07) [31]

И вообще - на фига тут FSemaphore: он же используется в контексте одного потока?


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

>Суслик ©   (14.10.04 16:12) [32]
Странная какая-то эта функция StartThreadFromQueue.

Как раз из-за проблем с синхронизацией она такая-)


 
Суслик ©   (2004-10-14 16:24) [34]


> Может быть, у кого еще будут предложения по синхронизации
> без использования объектов ядра, подобных Event

Ну, Александр, я же привел пример TMultiReadExclusiveWriteSynchronizer. Там то, что нужно. Только разбираться долго надо. Там нет объектов ядра, но есть синхронизация. Там все сделано на interloced функциях.


> >Суслик ©   (14.10.04 16:07) [31]
> Семафор удобен для счетчика потоков в пуле и сигнализирования
> о том, что есть свободные потоки.

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


> Как раз из-за проблем с синхронизацией она такая-)

Никаких проблем нет. Какая синхронизация? Потоками управляет ровно один поток - пуллер. Что хош, то и делай там.


 
Суслик ©   (2004-10-14 16:33) [35]

у рихтера есть про очередь потоков. Может там стоит посмотреть?


 
panov ©   (2004-10-14 16:56) [36]

Никаких проблем нет. Какая синхронизация? Потоками управляет ровно один поток - пуллер. Что хош, то и делай там.

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


 
Суслик ©   (2004-10-14 17:01) [37]


> Уже обсуждали, что Execute выполняется позде, чем происходит
> обюращение к прочим методам объекта.

ух... сложно как-то. Все-таки предложение увидеть модифицированный код, и продолжить обсуждение от новой отправной точки.


 
Суслик ©   (2004-10-14 17:07) [38]

Мне как-то не очень понятно, что эта за задача такая, где требуется такое титаническое быстродействие?


 
Суслик ©   (2004-10-14 17:09) [39]


> [36] panov ©   (14.10.04 16:56)
> Никаких проблем нет. Какая синхронизация? Потоками управляет
> ровно один поток - пуллер. Что хош, то и делай там.
>
> Уже обсуждали, что Execute выполняется позде, чем происходит
> обюращение к прочим методам объекта.

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


 
panov ©   (2004-10-14 17:24) [40]

Кстати, насчет TMultiReadExclusiveWriteSynchronize.

Как я понял, единственное, что мне даст, это то, что мне не надо несколько критических секций использовать, и объекты для ожидания создаются статически всего 2.



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

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

Наверх




Память: 0.61 MB
Время: 0.037 c
14-1098867372
Developerr
2004-10-27 12:56
2004.11.14
Чтение и запись файла с расширением dat


14-1098383173
SviL
2004-10-21 22:26
2004.11.14
Хочу найти компоненты


1-1099126770
namiq
2004-10-30 12:59
2004.11.14
vid formi


1-1099078938
Matrex
2004-10-29 23:42
2004.11.14
Экзотика - компонент VirtualTreeview


3-1098101578
OlegL
2004-10-18 16:12
2004.11.14
cxGrid





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