Форум: "Основная";
Текущий архив: 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));
Выполняется раньше, чем код в ExecutePeekMessage(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