Миграция с Indy 9 на 10 с Delphi, инициализация TIdSchedulerOfThreadPool
Я нахожусь в процессе обновления приложения Delphi с Indy 9 до Indy 10.
Это довольно больно, так как, видимо, многое изменилось.
Я застрял на одном шаге.
Вот старый код (работает с Indy 9):
Пул потоков создается, и каждый поток пула инициализируется, а затем запускается. Отдельные потоки создают независимый http-клиент (но это не имеет значения).
TUrlThread = class(TIdThread)
...
var
i: Integer;
begin
// create the Pool and init it
Pool := TIdThreadMgrPool.Create(nil);
Pool.PoolSize := Options.RunningThreads;
Pool.ThreadClass:= TUrlThread;
// init threads and start them
for i := 1 to Options.RunningThreads do
begin
with (Pool.GetThread as TUrlThread) do
begin
Index := i;
Controler := Self;
Priority := Options.Priority;
Start;
end;
end;
Класс TIdThreadMgrPool отсутствует в Indy 10.
Я искал замену, и TIdSchedulerOfThreadPool выглядит победителем, но я не могу запустить его.
Вот модифицированный (Indy 10) код:
TUrlThread = class(TIdThreadWithTask)
...
var
i: Integer;
begin
// create the Pool and init it
Pool := TIdSchedulerOfThreadPool.Create(nil);
Pool.PoolSize := Options.RunningThreads;
Pool.ThreadClass:= TUrlThread;
// init threads and start them
for i := 1 to Options.RunningThreads do
begin
with (Pool.NewThread as TUrlThread) do
begin
Index := i;
Controler := Self;
Priority := Options.Priority;
Start;
end;
end;
Я получаю исключение нарушения прав доступа здесь (это инди-код):
procedure TIdTask.DoBeforeRun;
begin
FBeforeRunDone := True;
BeforeRun;
end;
FBeforeRunDone равен нулю.
1 ответ
Вы правы в том, что TIdSchedulerOfThreadPool
замена Indy 10 для TIdThreadMgrPool
, Однако, что вы не принимаете во внимание, так это то, что TIdScheduler
архитектура немного отличается от TIdThreadMgr
архитектура.
В Индии 10 TIdThreadWithTask
не работает само по себе. Как следует из названия, TIdThreadWithTask
выполняет задачу, которая является TIdTask
объект (например, TIdContext
, которая является заменой Indy 10 для TIdPeerThread
) который связывается с потоком. Вы запускаете потоки, не давая им задач для выполнения, поэтому у вас возникают сбои. Для того, чтобы позвонить Start()
вручную, вам нужно сначала создать и назначить TIdTask
на основе объекта к TIdThreadWithTask.Task
имущество. TIdTCPServer
обрабатывает это, вызывая TIdScheduler.AcquireYarn()
создать TIdYarn
объект, который связан с TIdThreadWithTask
объект, а затем создает TIdContext
объект и передает его TIdScheduler.StartYarn()
, который использует TIdYarn
чтобы получить доступ к TIdThreadWithTask
назначить его Task
собственность до того звонка Start()
в теме.
Однако еще не все потеряно. И в Indy 9, и в 10 вы действительно не должны звонить TIdThread.Start()
вручную для начала. TIdTCPServer
обрабатывает это для вас после принятия нового клиентского соединения, получения потока из его ThreadMgr
/Scheduler
и связывание клиентского соединения с потоком. Вы можете инициализировать свойства потока по мере необходимости, фактически не запуская потоки немедленно. Свойства вступят в силу, когда потоки начнут выполняться позже.
Попробуй это:
TUrlThread = class(TIdThread)
...
var
i: Integer;
begin
// create the Pool and init it
Pool := TIdThreadMgrPool.Create(nil);
Pool.PoolSize := Options.RunningThreads;
Pool.ThreadClass:= TUrlThread;
Pool.ThreadPriority := Options.Priority;
// init threads and start them
for i := 1 to Options.RunningThreads do
begin
with (Pool.GetThread as TUrlThread) do
begin
Index := i;
Controler := Self;
end;
end;
,
TUrlThread = class(TIdThreadWithTask)
...
var
i: Integer;
begin
// create the Pool and init it
Pool := TIdSchedulerOfThreadPool.Create(nil);
Pool.PoolSize := Options.RunningThreads;
Pool.ThreadClass:= TUrlThread;
Pool.ThreadPriority := Options.Priority;
// init threads and start them
for i := 1 to Options.RunningThreads do
begin
with (Pool.NewThread as TUrlThread) do
begin
Index := i;
Controler := Self;
end;
end;
Теперь, с учетом сказанного, последнее, на что нужно обратить внимание. В Indy 9 и 10 потоки могут быть не возвращены в пул после завершения, а новые потоки добавлены в пул после выполнения кода инициализации. PoolSize
минимальное количество потоков в пуле, а не абсолютное число Больше, чем PoolSize
Количество клиентов может подключиться к серверу, и он с радостью создаст для них больше потоков в то время, когда они необходимы, таким образом обходя ваш код инициализации. В обеих версиях лучшее место для инициализации ваших тем - это TUrlThread
конструктор. Храните свой Controler
указатель где-то, что конструктор может достичь его при необходимости. И не имеет смысла назначать Index
каждому потоку, так как порядок потоков в пуле динамически меняется со временем.
Фактически, ваш код инициализации вручную на самом деле не подходит для обеих версий по другой причине. И то и другое TIdThreadMgrPool.GetThread()
а также TIdSchedulerOfThreadPool.NewThread()
не добавляйте новый поток в пул вообще. Потоки добавляются в пул как в Indy 9, так и в 10, когда поток перестает работать, и есть место для сохранения потока для повторного использования, и дополнительно в Indy 10 только тогда, когда TIdTCPServer
запускается. Таким образом, вы фактически создаете потоки, которые на самом деле ничего не делают и не отслеживаются пулом. Все больше причин для изменения дизайна кода инициализации в обеих версиях, чтобы потоки инициализировали себя при создании в нормальных условиях, а не взламывали архитектуру, чтобы создать их вручную.