Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Пул потоков.


Страницы:   1 2 3 4 5 6

panov ©   (2004-10-14 14:34:00) 

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


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;


Суслик   (2004-10-14 19:28:00) [41]

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

Вот простой пример (не относится к теме, но показывает устремленность людей, как писать оптимально)


function TStringList.Find(const S: string; var Index: Integer): Boolean;
var
 L, H, I, C: Integer;
begin
 Result := False;
 L := 0;
 H := FCount - 1;
 while L <= H do
 begin
   I := (L + H) shr 1;
   C := CompareStrings(FList^[I].FString, S);
   if C < 0 then L := I + 1 else
   begin
     H := I - 1;
     if C = 0 then
     begin
       Result := True;
       if Duplicates <> dupAccept then L := I;
     end;
   end;
 end;
 Index := L;
end;

Как вы думаете, почему не написано так (мне какжется так наглядней)

   if C < 0 then L := I + 1 else
   if C > 0 then  H := I - 1 else
   if C = 0 then
   begin
     Result := True;
     if Duplicates <> dupAccept then L := I;
   end;


Все таже пресловутая оптимальность - экономия на одном сравнении (в первом случае нет if c > 0). Понимаю, что пример оффтоповый, но мне кажется, что он показывает, что приемы оптимального программирования так просто не изобретешь - нужно планомерное чтение разной лит-ры и просмотр кода.


Суслик   (2004-10-14 19:20:00) [42]


>  [69] panov ©   (14.10.04 19:17)

Мое мнение такое (повторяюсь) - пока не будет тестов производительности, говорить об оптимальности или отсутствии таковой рано.

У меня нет сейчас времени на опыты :( Иначе подготовил бы статистический отчет. При этом надо тестировать не просто бомбардировку ОЯ (т.е. активное их использование), а имитацию реальной нагрузки на ОЯ, (т.е. попытаться смоделировать действительность - как часто будут ОЯ использоваться в зависимости от поребностей пользователя пуллера). Уверяю, что результат удивит - разница в общем производительности будет заметна слабо (конечно, если не брать каких-то экстремальных случаев).


panov   (2004-10-14 19:17:00) [43]

>Суслик ©   (14.10.04 19:13) [68]
Суперуниверсальный не нужен, конечно.

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

Кстати, даже для задачи, которой сейчас я занимаюсь(резервное копирование файлов), желательна минимальная нагрузка на процессор-)


Суслик   (2004-10-14 19:13:00) [44]


> Такой метод применим только для гарантированно кратких по
> времени ожиданий.

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

Или хочестся создать суперуниверсальный супероптимальный супервсеоблемющий пул для всех случае жизни? Это миф (имхо). Кадой задаче свое решение.


panov   (2004-10-14 19:11:00) [45]

>Суслик ©   (14.10.04 19:08) [64]

ну да, но это же лучше чем бегать из режима ядра и обратно, как бывает при использовании ОЯ

Пример "почти -)" спин-блокировки привел Verg ©   (14.10.04 18:51) [57]
while lacked do sleep(1)
Вот классический пример:
while lacked do ;

Естественно, такой код загрузит на время ожидания процессор на 100%.

Такой метод применим только для гарантированно кратких по времени ожиданий.


Суслик   (2004-10-14 19:11:00) [46]


> При небольших изменениях в коде код в начале топика будет
> совершенно работоспособным, другое дело, что все-таки хочется
> сделать оптимальный код-)

Когда сделаем, хорошо было бы его здесь увидеть :)
Т.е. я поддерживаю ЮрияК


Суслик   (2004-10-14 19:08:00) [47]


> InterLocked-функции могут быть применены только для спин-блокировки(активного
> ожидания).

ну да, но это же лучше чем бегать из режима ядра и обратно, как бывает при использовании ОЯ?

ЗЫ
Скаже всем, что понятия не имею, что такое режим ядра. Знаю только, что в него надо переходить для использования ОЯ и это дорогостоящая операция (несколько 1000 тактов). Это я говорю для того, чтобы не приписвать себе знания, коих нет. Но думаю понимание того, что лучше режим ядра избегать являтеся достаточным основанием для продолжения моего участия в дискусе.
Спасибо за внимание.


panov   (2004-10-14 19:08:00) [48]

>ЮрийК ©   (14.10.04 18:53) [59]

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


panov   (2004-10-14 19:05:00) [49]

>Суслик ©   (14.10.04 19:00) [61]
Например. interlocked функции еще есть.

InterLocked-функции могут быть применены только для спин-блокировки(активного ожидания).


panov   (2004-10-14 19:04:00) [50]

>ЮрийК ©   (14.10.04 18:53) [59]

Просьба выложить окнчательный вариант ваших изысканий, как только все выскажут свои замечания.

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

Конечно, вопрос с максимально работой и ожиданием без спин-блокировки все равно остается.

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


Суслик   (2004-10-14 19:00:00) [51]


> [57] Verg ©   (14.10.04 18:51)


> Вот и интересно как?


> while lacked do sleep(1)

Например. interlocked функции еще есть.


Суслик   (2004-10-14 18:56:00) [52]


> [57] Verg ©   (14.10.04 18:51)

Interlocedфункции, они же не ОЯ.
И в тоже время позволяют делать синхнонизацию. Класс TThreadLocalCounter например.

Контрольный вопрос.
В классе TThreadLocalCounter используются методы синхронизации. Например, TThreadLocalCounter.Open. Есть ли здесь ОЯ?


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


> Запусти и посмотри количество объектов ядра до нажатия кнопки
> и после. Предупреждаю, процесс лучше снимать из среды, по
> Ctrl+F2

Как?
Никогда не делал.


ЮрийК   (2004-10-14 18:53:00) [54]

Просьба выложить окнчательный вариант ваших изысканий, как только все выскажут свои замечания.


Verg   (2004-10-14 18:51:00) [55]


> [51] Суслик ©   (14.10.04 18:38)
>
> >  [50] Verg ©   (14.10.04 18:37)
>
> может и путаю. Но опыт сравнения быстродействия mutex и
> кс в свое время сказал мне о многом.


Не, ну сравнивать производительнось при различных способах использования ОЯ - это уже отдельный вопрос.

Вроде шла речь об обеспечении синхронизации БЕЗ использования ОЯ вообще?

> Мое утверждение было о том, что при синхронизации не обязательно
> использовать ОЯ.


Вот и интересно как?

while lacked do sleep(1)
?

Кстати, Mutex хорош своим ObjectName, т.е. он именованый, а значит синхронизировать по "принципу КС" можно потоки из разных процессов. Вот и все, если этого не требуется, то используй КС. Конечно она эффективнее в таком случае


Игорь Шевченко   (2004-10-14 18:49:00) [56]

Суслик ©   (14.10.04 18:45) [53]


> Сначала на вопрос ответь, где посмотреть реализацию КС?


windows.pas разумеется


Игорь Шевченко   (2004-10-14 18:48:00) [57]

Суслик ©   (14.10.04 18:30) [49]

Пример:

unit main;

interface
uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
 StdCtrls;

type
 TForm1 = class(TForm)
   Button1: TButton;
   procedure Button1Click(Sender: TObject);
 end;

var
 Form1: TForm1;

implementation
uses
 Thread;

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
 InitializeCriticalSection(CS);
 TMyThread.Create (false);
 TMyThread.Create (false);
end;

end.


и

unit Thread;

interface

uses
 Classes,
 Windows;

type
 TMyThread = class(TThread)
 private
 protected
   procedure Execute; override;
 end;

var
 CS: TRtlCriticalSection;

implementation

{ TMyThread }

procedure TMyThread.Execute;
begin
 EnterCriticalSection(CS);
end;

end.


Запусти и посмотри количество объектов ядра до нажатия кнопки и после. Предупреждаю, процесс лучше снимать из среды, по Ctrl+F2


Суслик   (2004-10-14 18:47:00) [58]


> [52] Игорь Шевченко ©   (14.10.04 18:42)

Вместо того, чтобы ко мне придираться (:))) ответил бы Панову - на первый вопрос [23]. Все польза была бы.


Суслик   (2004-10-14 18:45:00) [59]


> Что есть коллизия по Рихтеру ?

Сначала на вопрос ответь, где посмотреть реализацию КС?

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


Игорь Шевченко   (2004-10-14 18:42:00) [60]

Суслик ©   (14.10.04 18:30) [49]

Что есть коллизия по Рихтеру ?


Страницы:   1 2 3 4 5 6

Наверх

Титульная страница Поиск, карта сайта Написать письмо