Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.12.05;
Скачать: CL | DM;

Вниз

Нужна критика кода-)   Найти похожие ветки 

 
jack128 ©   (2004-11-12 00:28) [40]

GuAV ©   (12.11.04 0:21) [37]
Application.Processmessages

хм, да. Но это все таки тоже исключение. С этом методом надо очень осторожно оброщаться.


 
GuAV ©   (2004-11-12 00:28) [41]

jack128 ©   (12.11.04 0:26) [38]
Да с Release локальная переменная никогда не станет инвалидной


И при Application.Processmessages ?

jack128 ©   (12.11.04 0:26) [38]
Вообще - имеет ли объект право уничтожать себя сам?

Некоторые объекты имеют => в общем случае IMHO да.


 
Игорь Шевченко ©   (2004-11-12 00:36) [42]

GuAV ©   (12.11.04 00:26) [39]

А ведь точно, спутал с наследниками. Которые устанавливают у себя FreeOnTerminate. Спасибо :)

GuAV ©   (12.11.04 00:21) [37]

До кучи вспомним еще TFrom.Close с Action равной caFree.

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


 
Verg ©   (2004-11-12 01:02) [43]


> Но у меня все-таки вопрос к автору - в контекте какого потока
> выполняется его метод Kill


Глаавно, что это противоречие неустранимо.
Т.е Kill должен быть гарантированно вызван не в контексте потока, которым управляет этот объект. Иначе - либо потеря самого объекта (мемлик), либо потеря объекта ядра из-за незакрытия Handle этого потока.

Второе "либо" относится к такому "эндшпилю":

procedure TThreadClass.Kill(ExitCode: Cardinal);
var H : THandle;
begin
 H := FHandle;
 Free;
// При учете, что CloseHande(FHandle) перенесли из деструктора в ThreadProcedure перед ExitThread;
 TerminateThread( H, ExitCode );
// Напрашивающийся тут мат в один ход уже никому не нужен :))
 CloseHandle(H); // - Unreachable Code, хотя кто-то должен же закрыть этот хендель
end;

Да, и кому может понадобиться вызов Terminate в оригинальном коде Kill ? :)))


 
GuAV ©   (2004-11-12 01:20) [44]

Verg ©   (12.11.04 1:02) [43]

Целый поток был грубо прибит, что настолько плохо что не думаю что один незакрытый хэндл сделает большую разницу :)


 
Игорь Шевченко ©   (2004-11-12 01:24) [45]

GuAV ©   (12.11.04 01:20) [44]

Считается ли прибитым поток, если у него остался незакрытый Handle ? :)


 
GuAV ©   (2004-11-12 01:40) [46]

Игорь Шевченко ©   (12.11.04 1:24) [45]
Нет, он не убитый, это подранок :)

Короче, таки надо так:
if GetCurrentThreadId = FThreadId the
 ExitThread(...)
else
 TerminateThread(...)
...


 
GuAV ©   (2004-11-12 01:47) [47]

2 Verg ©
Кстати, а как же тогда реализован TThread.FreeOnTerminate


 
panov ©   (2004-11-12 02:00) [48]

Хочу все-же пояснить про Kill.

Объект создает поток.
В этом объекте есть метод Kill.
Метод Kill:
1. Убивает поток (TerminateThread), но не объект!
2. Устанавливает флаг завершения для того, чтобы любой наследник(не он сам) мог видеть, что поточная функция завершена(в процессе завершения) и нет смысла в этом смысле передавать/принимать от объекта какие-либо данные.
3. Независимо от значения FreeOnTerminate вызывает деструктор Destroy, который закрывает 2 созданных дескриптора в объекте.

Т.е. метод Kill позволяет корректно уничтожить сам объект и освободить все ресурсы, за исключением созданных в поточной функции.

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


 
Игорь Шевченко ©   (2004-11-12 10:18) [49]

panov ©   (12.11.04 02:00) [48]

[43] читал ?

В контексте какого потока будет выполняться метод Kill ?


 
Владислав ©   (2004-11-12 10:21) [50]

Таки на вопрос Игоря не ответили :)


 
Cobalt ©   (2004-11-12 10:41) [51]

2 Игорь Шевченко ©   (12.11.04 10:18) [49]
Т.к. Александр позиционирует этот метод, как
для простого уничтожения зависшего потока
,то не из самого потока :)


 
Игорь Шевченко ©   (2004-11-12 10:46) [52]

Cobalt ©   (12.11.04 10:41) [51]

Тогда все мои возражения остаются в силе. Код абсолютно нерабочий.


 
Digitman ©   (2004-11-12 11:05) [53]


> panov ©   (11.11.04 21:28) [11]
> Этот класс хочу использовать в пуле потоков. Он должен быть
> максимально простым и управляемым


Если уж на то пошло (пул здесь фигурирует), то лучше бы ты сосредоточил мозговые усилия на реализации трэд-класса, "заточенного" к использованию именно какими-то пул-менеджерами.. возьми за ориентир, скажем, все тот же TServerClientThread .. просто разработай свои TCustomThreadPoolMgr, TCustomThreadPool, TCustomPooledThread и сделай грамотную и надежную "связку" между
ними - это, пожалуй, будет гораздо полезней, нежели "курочить" TThread на свой манер

а по поводу простоты и управляемости - не вижу сколь-либо ощутимого повода для нареканий в отношении этого в станд.реализации TThread

imho


 
panov ©   (2004-11-12 12:45) [54]

>Игорь Шевченко ©   (12.11.04 10:46) [52]

Повторю: Kill сделан не для вызова из поточной функции.
Т.е. из любого другого потока, кроме созданного CreateThread в конструкторе.

Тогда все мои возражения остаются в силе. Код абсолютно нерабочий.

Можно пояснить, какой именно момент нерабочий и чем это вызвано?


 
Игорь Шевченко ©   (2004-11-12 12:56) [55]

panov ©   (12.11.04 12:45) [54]

Можно написать комментарии все-таки ?


 
PVOzerski ©   (2004-11-12 13:00) [56]

2[54]:
Тогда нужно проверять потоковую принадлежность кода, вызывающего Kill и "в случае чего" генерить исключение. IMHO, конечно. Но проверить-то можно. Exception выглядит убедительнее, чем рекомендации копаться в документации :^) Что-нибудь вроде

procedure TThreadClass.Kill(ExitCode: Cardinal);
begin
 if GetCurrentThreadId<>FThreadId then
  raise что нибудь;


 
panov ©   (2004-11-12 13:06) [57]

>Игорь Шевченко ©   (12.11.04 12:56) [55]

Сейчас исправлю с учетом замечаний и снова выложу.


 
REA   (2004-11-12 13:09) [58]

>Этот класс хочу использовать в пуле потоков

А используется Thread Pooling? (QueueUserWorkItem, BindIoCompletionCallback там всякие)


 
panov ©   (2004-11-12 13:10) [59]

>REA   (12.11.04 13:09) [58]

А в W95-WinNT?


 
panov ©   (2004-11-12 14:16) [60]


unit uThreadClass;

interface

uses
 classes,windows;

type

 TSimpleThreadObj=class
 private
   FHandle,FThreadId: THandle;       //Handle & ThreadId создаваемого потока
   FEvent: THandle;                  //Event для ожидания завершения инициализации
   FFreeOnTerminate: Boolean;        //Флаг автоматического разрушения объекта
                                     //  после окончания выполнения поточной функции
   FTerminated: Boolean;             //Флаг состояния завершения потока
   FSuspended: Boolean;              //Флаг состояния Thread Suspended
   FReturnCode: Integer;             //Код возврата поточной функции
   procedure OnStartThread;          //Установка состояния "Поток создан"
 protected
   procedure Execute;virtual;abstract;
 public
   constructor Create(const CreateSuspended: Boolean);
   destructor Destroy;override;

   procedure AfterConstruction; override; //Перекрытие AfterConstruction

   procedure OnEndThread;virtual;abstract; //Событие, возникнет перед окончанием
                                        //  поточной функции
   procedure Suspend;                   //Приостановить поток
   procedure Resume;                    //Разбудить поток
   procedure WaitFor(Timeout: Cardinal=INFINITE);  //Ждать завершения поточной
                                                   //  функции
   procedure Terminate;                //Установить флаг состояния завершения потока
   procedure Kill(ExitCode: Cardinal); //Завершить выполнение поточной функции

   property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate;
   property Terminated: Boolean read FTerminated;
   property Handle: THandle read FHandle;
   property ThreadId: THandle read FThreadId;
   property ReturnCode: Integer read FReturnCode write FReturnCode;
 end;

implementation

//function ThreadProcedure(const Sender: TObject): Cardinal; stdcall;
//
//  Поточная функция
//  Параметр Sender - ссылка на объект класса TSimpleThreadObj,
//    в котором создан поток
//
function ThreadProcedure(const Sender: TObject): Cardinal; stdcall;
begin
 Result := 0;                              //Устанавливаем код возврата в 0
 try
   TSimpleThreadObj(Sender).OnStartThread; //Поточная функция начала работать,
   TSimpleThreadObj(Sender).Execute;       //Собственно выполнение пользовательского кода
   TSimpleThreadObj(Sender).OnEndThread;   //Поточная функция законлила выполнение кода
 except
   Result := INVALID_HANDLE_VALUE;
 end;
//  Если установлен флаг автоматического уничтожения объекта - вызыыаем Free
 if TSimpleThreadObj(Sender).FreeOnTerminate then TSimpleThreadObj(Sender).Free;
end;

{ TSimpleThreadObj }

//procedure TSimpleThreadObj.AfterConstruction;
//
//  метод вызывается сразу после завершения конструктора
//  назначение: при необходимости дождаться, пока начнет работу поточная функция.
//
procedure TSimpleThreadObj.AfterConstruction;
begin
 inherited;
 //если поток создается не в приостановленном состоянии, ждем,
 //  пока будет установлен FEvent
 if not FSuspended then
 begin
   Resume;
   WaitForSingleObject(FEvent,INFINITE);
 end;
end;

//constructor TSimpleThreadObj.Create(const CreateSuspended: Boolean);
//
//  Конструктор
//  Параметры;
//    CreateSuspended: Boolean  - True - поток создается в приостановленном состоянии
//
constructor TSimpleThreadObj.Create(const CreateSuspended: Boolean);
begin
 inherited Create;
 FreeOnTerminate := True;                  //по умолчанию объект
                                           //  уничтожается автоматически
 FSuspended := CreateSuspended;
 FEvent := CreateEvent(nil,True,False,nil);
 FHandle := CreateThread(nil,0,@ThreadProcedure,Self,CREATE_SUSPENDED,FThreadId);
end;

//destructor TSimpleThreadObj.Destroy;
//
//  Деструктор
//
destructor TSimpleThreadObj.Destroy;
begin
 CloseHandle(FHandle);
 CloseHandle(FEvent);
 inherited;
end;

//procedure TSimpleThreadObj.OnStartThread;
//
//  метод вызывается после старта поточной функции
//
procedure TSimpleThreadObj.OnStartThread;
begin
// Устанавливаем Event в Sinaled - поточная функция начала выполняться
 if not FSuspended then SetEvent(FEvent);
end;

//procedure TSimpleThreadObj.Kill(ExitCode: Cardinal);
//
//  метод вызывается для "жесткого" уничтожения потока
//  TerminateThread(например, для уничтожения "зависшего" потока)
//
procedure TSimpleThreadObj.Kill(ExitCode: Cardinal);
begin
 if GetCurrentThreadId=FThreadId then Exit;

 Terminate;                              //Устанавливаем состояние завершения
 TerminateThread(FHandle,ExitCode);      //Уничтожаем поток
 if FreeOnTerminate then Free;
end;

//procedure TSimpleThreadObj.Resume;
//
//  "Пробуждение" потока
//
procedure TSimpleThreadObj.Resume;
begin
 ResumeThread(FHandle);
 FSuspended := False;
end;

//procedure TSimpleThreadObj.Suspend;
//
//  "Усыпление" потока
//
procedure TSimpleThreadObj.Suspend;
begin
 SuspendThread(FHandle);
 FSuspended := True;
end;

//procedure TSimpleThreadObj.Terminate;
//
//  устанавливаем флаг состояния завершения
//
procedure TSimpleThreadObj.Terminate;
begin
 FTerminated := True;
end;

//procedure TSimpleThreadObj.WaitFor(Timeout: Cardinal=INFINITE);
//
//  Ожидание завершения потока
//
procedure TSimpleThreadObj.WaitFor(Timeout: Cardinal=INFINITE);
begin
 WaitForSingleObject(FHandle,TimeOut);
end;

end.


 
panov ©   (2004-11-12 14:17) [61]

>Digitman ©   (12.11.04 11:05) [53]

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


 
Игорь Шевченко ©   (2004-11-12 14:23) [62]


>  procedure OnEndThread;virtual;abstract; //Событие, возникнет
> перед окончанием
>                                         //  поточной функции


Его надо ОБЯЗАТЕЛЬНО реализовать в потомках ?


 
GuAV ©   (2004-11-12 14:30) [63]

panov ©   (12.11.04 14:16) [60]
  procedure OnEndThread;virtual;abstract; //Событие, возникнет перед окончанием
                                       //  поточной функции

По имени и по коментарию событие, а по коду virtual;abstract; - абстрактный метод. Не хорошо. Вносит путаницу.


 
panov ©   (2004-11-12 14:50) [64]

>Игорь Шевченко ©   (12.11.04 14:23) [62]
Нет, не обязательно-)
заменил на
procedure OnEndThread;virtual;


 
Игорь Шевченко ©   (2004-11-12 14:52) [65]

panov ©   (12.11.04 14:50) [64]

Пост [63]. Лично у меня объявление чего-либо, начинающегося с On виртуальным вызывает подозрительность.

Call me paranoid but finding "/*" inside this comment makes me suspicious
(с)


 
panov ©   (2004-11-12 14:55) [66]

>Игорь Шевченко ©   (12.11.04 14:52) [65]
Пост [63]. Лично у меня объявление чего-либо, начинающегося с On виртуальным вызывает подозрительность.

Почему?-)
Так как тогда лучше назвать?-)


 
Игорь Шевченко ©   (2004-11-12 15:01) [67]


> Так как тогда лучше назвать?-)


Без On (кстати, к OnStartThread это тоже относится)


 
panov ©   (2004-11-12 15:11) [68]

>Игорь Шевченко ©   (12.11.04 15:01) [67]

Переобозвал.

procedure StartThread;
procedure EndThread;


 
GuAV ©   (2004-11-12 21:13) [69]

panov ©   (12.11.04 13:06) [57]
Сейчас исправлю с учетом замечаний и снова выложу.

panov ©   (11.11.04 23:24) [27]
IsMultiThread упустил из вида


 
panov ©   (2004-11-12 21:33) [70]

>GuAV ©   (12.11.04 21:13) [69]
IsMultiThread упустил из вида

Да, я уже заметил, спасибо.

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


 
panov ©   (2004-11-16 12:14) [71]

Наконец-то плюнул на собственную реализацию поточного класса(хотя и зря, может быть), реализовал первую рабочую версию пула потоков.
Конечно, не претендую на совершенство и маскимально оптимизированное решение, но эта версия хотя бы работает-)
(Версия нумбер 3).

Реализовано 4 класса:
TThreadPool - менеджер потоков.
TThreadsThr - собственно поток из пула.
TPushPop - потокобезопасная работа с очередью заданий на выполнение(двусвязный список).
TSafeArray - потокобезопасная работа с массивом указателей(динамический массив потоков).

Версия рабочая, но некоторые примочки еще можно добавлять.

Если кого-то интересует, то исходники могу выложить.
Вот только не знаю, сюда или нет. Код в 3-х модулях, 500 строк.


 
panov ©   (2004-11-16 12:40) [72]

Основной модуль.

unit uThreadPool;

interface

uses
 Classes, Windows, SysUtils, SyncObjs,
   uSafeArray, uPushPop;

type

 TProcThr=procedure(parm: Pointer);  //клиентская процедура для выполнения в пуле потоков

// Служебная структура для передачи параметров
 PJob=^TJob;
 TJob=record
   Proc: TProcThr;         //клиентская процедура
   Parm: Pointer;          //параметры процедуры
 end;

 TThreadsPool=class;

//  TThreadsThr=class(TThread)
//  Поток для выполнения в пуле
 TThreadsThr=class(TThread)
 private
   FThreadsPool: TThreadsPool;   //Ссылка на менеджер потоков
   FEvent: TSimpleEvent;         //Event для ожидания окончания инициализации
   FEventExec: TSimpleEvent;     //Event для управления циклом в Execute
   FProc: TProcThr;              //Клиентская процедура
   FParm: Pointer;               //Параметры клиентской процедуры
   FIsFree: Boolean;             //Флаг "свободности" потока
   constructor Create;           //Конструктор(не вызывается напрямую)
 protected
   procedure Execute; override;
 public
   destructor Destroy;override;
   class function CreateThr(Pool: TThreadsPool):TThreadsThr; //Функция для создания потока
   procedure SetJob(Proc: TProcThr;Parm: Pointer);           //Установка параметров потока

   property isFree: Boolean read FIsFree write FIsFree;      //Флаг "свободности" потока
 end;

//  TThreadsThr=class(TThread)
//  Пул потоков
 TThreadsPool=class(TThread)
 private
   FJobs: TPushPop;            //Очередь заданий на выполнение в пуле
   FPool: TSafeArray;          //Пул потоков
   FMaxThreads: Integer;       //Макс. количество потоков в пуле
   FEvent: TSimpleEvent;       //Event для ожидания окончания инициализации пула
   FEventAddJob: TSimpleEvent; //Event для Управления циклом в Execute
   constructor Create(const MaxThreads: Integer); //Конструктор(не вызывается напрямую)
   function GetFreeThr: Integer; //Поиск свободного потока в пуле
   procedure EndJob(Sender: TThreadsThr); //обработка завершения выполнения задания
   procedure EndThread(Sender: TThreadsThr); //Обработка завершения потока в пуле
   function GetCountJobs: Integer;           //Количество заданий в очереди
   function GetCountThreads: Integer;        //Количество потоков в пуле
   function GetCountFreeThr: Integer;        //Количество свобожных потоков в пуле
 protected
   procedure Execute; override;
 public
   destructor Destroy;override;
   class function CreatePool(const MaxThreads: Integer):TThreadsPool; //Функция для создания пула

   procedure AddJob(Proc: TProcThr;Parm: Pointer); //Добавление задания в очередь
   property CountJobs: Integer read GetCountJobs;  //Количество заданий в очереди
   property CountThreads: Integer read GetCountThreads;  //Количество потоков в пуле
   property CountFreeThr: Integer read GetCountFreeThr;  //Количество свобожных потоков в пуле
 end;

implementation

{ TThreadsPool }

constructor TThreadsPool.Create(const MaxThreads: Integer);
begin
   inherited Create(True);
   FreeOnTerminate := True;
   FMaxThreads := MaxThreads;
   FEvent := TSimpleEvent.Create;
   FEventAddJob := TSimpleEvent.Create;
   FEvent.ResetEvent;
   FJobs := TPushPop.Create;   //Очередь заданий
   FPool := TSafeArray.Create; //Массив(пул) потоков
end;

class function TThreadsPool.CreatePool(const MaxThreads: Integer):TThreadsPool;
begin
 Result := TThreadsPool.Create(MaxThreads);
 Result.Resume;
 case Result.FEvent.WaitFor(INFINITE) of //ждем окончания инициализации
   wrSignaled: Exit;
   wrTimeOut,
   wrAbandoned,
   wrError:
     begin
       Result.Terminate;
       Result := nil;
       Exit;
     end;
 end;
end;

destructor TThreadsPool.Destroy;
var
 i: Integer;
begin
 if Assigned(FEvent) then FEvent.Free;
 if Assigned(FEventAddJob) then FEventAddJob.Free;
//Очистка очереди заданий
 while FJobs.Count<>0 do Dispose(PJob(FJobs.Pop));
 FJobs.Free;
//Освобождение потоков в пуле
 for i := 0 to FPool.Count-1 do TThreadsThr(FPool[i]).Terminate;
 FPool.Free;
 inherited;
end;

procedure TThreadsPool.Execute;
var
 RC: TWaitResult;
 Thr: TThreadsThr;
 NumThr: Integer;
 Job: PJob;
begin
//Странное какое-то состояние. - Terminate
 if FMaxThreads=0 then
 begin
   Terminate;
   Exit;
 end;

 FEvent.SetEvent;  //Инициализация закончена, сообщаем.

 while not Terminated do
 begin
//Ждем наступления события "Есть задание в очереди"
   RC := FEventAddJob.WaitFor(100);
   if Terminated then break;
   case RC of
     wrSignaled :  //Появилось задание
       begin
         FEventAddJob.ResetEvent;        //Сбрасываем Event
         NumThr := GetFreeThr;           //Получить номер свободного потока
//Проверяем наличие заданий в очереди, пока есть свободные потоки
//  и задания
         while (FJobs.Count<>0) and (NumThr<>-1) do
         begin
           if NumThr=-1 then Continue;   //Нет свободных потоков
           Thr := FPool.Items[NumThr];
           Thr.isFree := False;          //Флаг "Поток занят"
           Job := FJobs.Pop;             //Извелекаем задание из очереди
           Thr.SetJob(Job^.Proc,Job.Parm); //Устанавливаем параметры для выполнения
           Dispose(Job);                   //Освободить память
           Thr.FEventExec.SetEvent;        //Известить поток о готовности к выполнению
           NumThr := GetFreeThr;           //Получить номер свободного потока
         end;
         Continue;
       end;
     wrTimeout:
       begin
         FEventAddJob.SetEvent;            //Проверить очередь заданий
       end;
   else
     begin
       Terminate;                         //Ошибка(??) завершаем работу
     end;
   end;
 end;

end;

procedure TThreadsPool.AddJob(Proc: TProcThr; Parm: Pointer);
var
 Job: PJob;
begin
 New(Job);
 Job^.Proc := @Proc;
 Job^.Parm := Parm;
 FJobs.Push(Job);        //Поместить задание в очередь
 FEventAddJob.SetEvent;  //Обработать очередь заданий(в Execute)
end;

function TThreadsPool.GetFreeThr: Integer;
var
 i: Integer;
begin
 Result := -1;
 for i := 0 to FPool.Count-1 do
 begin
//Проверка флага "Поток свободен"
   if TThreadsThr(FPool[i]).isFree then
   begin
     Result := i;
     break;
   end;
 end;
 if Result=-1 then       //Нет свободных потоков
 begin
   if FPool.Count<FMaxThreads then //Количество потоков меньше максимального значения?
   begin
     Result := FPool.Add(TThreadsThr.CreateThr(Self)); //Создаем новый поток в пуле
   end;
 end;
end;

procedure TThreadsPool.EndJob(Sender: TThreadsThr);
begin
 Sender.IsFree := True;  //Устанавливаем флаг "Поток свободен"
 FEventAddJob.SetEvent;  //Обработать очередь заданий(в Execute)
end;

procedure TThreadsPool.EndThread(Sender: TThreadsThr);
begin
 FPool.Delete(FPool.IndexOf(Sender));  //Удалить поток из пула
end;


 
panov ©   (2004-11-16 12:41) [73]


function TThreadsPool.GetCountJobs: Integer;
begin
 Result := FJobs.Count;  //Количество заданий в очереди
end;

function TThreadsPool.GetCountThreads: Integer;
begin
 Result := FPool.Count;  //Количество потоков в пуле
end;

function TThreadsPool.GetCountFreeThr: Integer;
var
 i: Integer;
begin
//Количество свободных потоков в пуле
 Result := 0;
 for i := 0 to FPool.Count-1 do
 begin
   if TThreadsThr(FPool[i]).IsFree then Inc(Result);
 end;
end;

{ TThreadsThr }

constructor TThreadsThr.Create;
begin
 inherited Create(True);
 FreeOnTerminate := True;
 FEvent := TSimpleEvent.Create;
 FEventExec := TSimpleEvent.Create;
 IsFree := False;
 FEvent.ResetEvent;
end;

class function TThreadsThr.CreateThr(Pool: TThreadsPool): TThreadsThr;
begin
 Result := TThreadsThr.Create;
 Result.FThreadsPool := Pool;
 Result.Resume;
 case Result.FEvent.WaitFor(INFINITE) of
   wrSignaled: Exit;
   wrTimeOut,
   wrAbandoned,
   wrError:
     begin
       Result.Terminate;
       Result := nil;
       Exit;
     end;
 end;
end;

destructor TThreadsThr.Destroy;
begin
 if Assigned(FEvent) then FEvent.Free;
 if Assigned(FEventExec) then FEventExec.Free;
 inherited;
end;

procedure TThreadsThr.SetJob(Proc: TProcThr;Parm: Pointer);
begin
 FProc := Proc;
 FParm := Parm;
end;

procedure TThreadsThr.Execute;
var
 RC: TWaitResult;
begin
 FEvent.SetEvent;
 isFree := True;
 while not Terminated do
 begin
   RC := FEventExec.WaitFor(100);
   FEventExec.ResetEvent;
   case RC of
     wrSignaled:
       begin
         try
           isFree := False;
           FProc(FParm);   //Выполняем клиентскую процедуру
         except
         end;
         FThreadsPool.EndJob(Self);  //Извещаем менеджер потоков об окончании
       end;
     wrTimeout:
       begin
       end;
     else
     begin
       FThreadsPool.EndThread(Self); //Извещаем менеджер потоков о завершении потока
       Terminate;
     end;
   end;
 end;
end;

end.


 
panov ©   (2004-11-16 12:41) [74]


{
 Защищенный связный список для работы с очередью
 в многопоточном режиме(первый вошел-первый вышел)

}
unit uPushPop;

interface

uses
 windows;
type

 PItemList=^TItemList;
 TItemList=record
   Prev,Next: PItemList;
   Data: Pointer;
 end;

 TPushPop=class
 private
   FCS: RTL_CRITICAL_SECTION;
   FRootItem: PItemList;
   FLastItem: PItemList;
   FItemsCount: Integer;
   procedure Lock;
   procedure Unlock;
   procedure DeleteItem0;
   function GetCount: Integer;
 public
   constructor Create;
   destructor Destroy;override;
   procedure Push(const aObject: Pointer);
   function Pop: Pointer;
   function Peek: Pointer;
   property Count: Integer read GetCount;
 end;

implementation

{ TPushPop }

constructor TPushPop.Create;
begin
 New(FRootItem);
 New(FLastItem);

 FRootItem.Prev := nil;
 FRootItem.Next := FLastItem;
 FRootItem.Data := nil;
 FLastItem.Prev := FRootItem;
 FLastItem.Data := nil;
 FLastItem.Next := nil;

 FItemsCount := 0;
 InitializeCriticalSection(FCS);
end;

destructor TPushPop.Destroy;
begin
 Lock;
   while FRootItem.Next<>FLastItem do DeleteItem0;
   Dispose(FRootItem);
   Dispose(FLastItem);
 Unlock;
end;

procedure TPushPop.Lock;
begin
 EnterCriticalSection(FCS);
end;

procedure TPushPop.Unlock;
begin
 LeaveCriticalSection(FCS);
end;

procedure TPushPop.DeleteItem0;
var
 p: PItemList;
begin
 if FItemsCount=0 then Exit;
 p := FRootItem.Next;

 p.Prev.Next := p.Next;
 p.Next.Prev := p.Prev;

 Dispose(p);
 Dec(FItemsCount);
end;

function TPushPop.Peek: Pointer;
begin
 Lock;
 try
   Result := FRootItem.Next.Data;
 finally
   Unlock;
 end;
end;

function TPushPop.Pop: Pointer;
begin
 Lock;
 try
   Result := FRootItem.Next.Data;
   DeleteItem0;
 finally
   Unlock;
 end;
end;

procedure TPushPop.Push(const aObject: Pointer);
var
 p: PItemList;
begin
 Lock;
 try
   New(p);
   p.Data := aObject;

   p.Prev := FLastItem.Prev;
   p.Next := FLastItem;
   FLastItem.Prev.Next := p;
   FLastItem.Prev := p;

   Inc(FItemsCount);
 finally
   Unlock;
 end;

end;

function TPushPop.GetCount: Integer;
begin
 Lock;
 try
   Result := FItemsCount;
 finally
   Unlock;
 end;
end;

end.


 
panov ©   (2004-11-16 12:42) [75]


{
 TSafeArray=class

 Класс для работы с дин. массивом
   в многопоточном приложении
}
unit uSafeArray;

interface

uses
 windows;
type

 TSafeArray=class
 private
   FCS: RTL_CRITICAL_SECTION;    //Крит. секция для защиты массива
   FArray: array of Pointer;

   procedure Lock;               //Вход в крит. секцию
   procedure Unlock;             //Выход из крит. секции
   function GetItem(const Index: Integer): Pointer;
   procedure SetItem(const Index: Integer; const Value: Pointer);
 public
   constructor Create;
   destructor Destroy; override;

   function Add(const Item: Pointer): Integer;
   function IndexOf(const Item: Pointer): Integer;
   function Count: Integer;
   procedure Delete(const Index: Integer);
   procedure Clear;

   property Items[const Index: Integer]: Pointer read GetItem write SetItem;default;
 end;

implementation

{ TSafeArray }

function TSafeArray.Add(const Item: Pointer): Integer;
begin
 Lock;
 Result := Length(FArray);
 SetLength(FArray,Result+1);
 FArray[Result] := Item;
 Unlock;
end;

procedure TSafeArray.Clear;
begin
 Lock;
 SetLength(FArray,0);
 Unlock;
end;

function TSafeArray.Count: Integer;
begin
 Result := Length(FArray);
end;

constructor TSafeArray.Create;
begin
 InitializeCriticalSection(FCS);
end;

procedure TSafeArray.Delete(const Index: Integer);
var
 i: Integer;
 Len: Integer;
begin
 Len := Length(FArray);
 if Index<0 then Exit;
 if Index>Len-1 then Exit;
 Lock;
 for i := Index+1 to Len-1 do
 begin
   FArray[i-1] := FArray[i];
 end;
 SetLength(FArray,Len-1);
 Unlock;
end;

destructor TSafeArray.Destroy;
begin
 DeleteCriticalSection(FCS);
 SetLength(FArray,0);
 inherited;
end;

function TSafeArray.GetItem(const Index: Integer): Pointer;
begin
 Lock;
 Result := FArray[Index];
 Unlock;
end;

function TSafeArray.IndexOf(const Item: Pointer): Integer;
var
 i: Integer;
begin
 Result := -1;
 Lock;
 for i := 0 to Length(FArray)-1 do
 begin
   if Item=FArray[i] then
   begin
     Result := i;
     break;
   end;
 end;
 Unlock;
end;

procedure TSafeArray.Lock;
begin
 EnterCriticalSection(FCS);
end;

procedure TSafeArray.SetItem(const Index: Integer; const Value: Pointer);
begin
 if (Index<0) or (Index>Length(FArray)-1) then Exit;
 Lock;
 FArray[Index] := Value;
 Unlock;
end;

procedure TSafeArray.Unlock;
begin
 LeaveCriticalSection(FCS);
end;

end.


 
panov ©   (2004-11-16 12:46) [76]

Пример использования:

type
 PRec=^Rec;
 Rec=record
   n: Integer;
   Number: Integer;
 end;

var
 Pool: TThreadsPool;

procedure Exec(parm: Pointer);
var
 R: PRec;
begin
 R := parm;
 Sleep(R.n);
//здесь нужна синхронизация
 fMain.lb.Items.Add(IntToStr(R.Number)+":"+IntToStr(R.n));
 Dispose(R);
end;

//Создание пула
procedure TfMain.Button1Click(Sender: TObject);
begin
 Pool := TThreadsPool.CreatePool(100);
end;

//Создание заданий на выполнение
procedure TfMain.Button2Click(Sender: TObject);
var
 r: PRec;
 i: Integer;
begin
 Randomize;
 for i := 0 to 1000 do
 begin
   New(r);
   r.Number := i;
   r.n := Random(1000)*10+100;
   Pool.AddJob(@Exec,r);
 end;
end;


 
Digitman ©   (2004-11-16 14:11) [77]


> panov


а стоило ли городить огород с собственной реализацией thread-safe-очереди (TPushPop) ?

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

опять же и TSafeArray это касаемо ...
не вижу резона вводить thread-safe-дин.массив вместо стандартного TThreadList .. к тому же бесконечные реаллокации памяти при операциях с дин.массивом ощутимо повлияют на общую производительность алгоритма..

и вот еще что бросается в глаза - ты делаешь Lock, а нижеследующий Unlock у тебя небезусловен, что рано или поздно приведет к коллизиям из-за потенциально возможных исключениях между Lock и Unlock


 
panov ©   (2004-11-16 14:39) [78]


> и вот еще что бросается в глаза - ты делаешь Lock, а
> нижеследующий Unlock у тебя небезусловен, что рано или
> поздно приведет к коллизиям из-за потенциально
> возможных исключениях между Lock и Unlock


Я не заметил, где такое. Если есть, то это ошибка.

Вроде бы как везде такой формат:

Lock;
try
finally
 Unlock;
end;


 
panov ©   (2004-11-16 14:41) [79]

есть же TThreadList, можно же было или унаследоваться от него или включить его как член класса TPushPop

Ну не нравится мне этот класс, к сожалению.
Не лежит душа к использованию его.


 
Digitman ©   (2004-11-16 14:42) [80]

ну здрасть-приехали !

а это что

function TSafeArray.GetItem(const Index: Integer): Pointer;
begin
Lock;
Result := FArray[Index];
Unlock;
end;

?



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

Текущий архив: 2004.12.05;
Скачать: CL | DM;

Наверх




Память: 0.7 MB
Время: 0.052 c
1-1100777576
_Freshman_
2004-11-18 14:32
2004.12.05
Написание обработчика OnTerminate


1-1101140982
anat
2004-11-22 19:29
2004.12.05
изолинии


14-1100074668
Суслик
2004-11-10 11:17
2004.12.05
Где купить delphi6 со всеми сервис паками?


14-1100692895
Ega23
2004-11-17 15:01
2004.12.05
А что есть OnClick?


1-1100890969
AlexHawk
2004-11-19 22:02
2004.12.05
апуск и останов таймера кнопками с клавы??