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

Вниз

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

 
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;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.7 MB
Время: 0.043 c
1-1100675286
Jay
2004-11-17 10:08
2004.12.05
Help Button


3-1099884978
UVV
2004-11-08 06:36
2004.12.05
Компоненты для работы с Oracle


4-1098694249
Wolffgang
2004-10-25 12:50
2004.12.05
Серийник винта.


3-1099827234
naum
2004-11-07 14:33
2004.12.05
Пробежка по всей базе MS Access (mdb)


1-1100803888
self001
2004-11-18 21:51
2004.12.05
file of record





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