![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Пул потоков.
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]
Что есть коллизия по Рихтеру ?