Миграция с 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 запускается. Таким образом, вы фактически создаете потоки, которые на самом деле ничего не делают и не отслеживаются пулом. Все больше причин для изменения дизайна кода инициализации в обеих версиях, чтобы потоки инициализировали себя при создании в нормальных условиях, а не взламывали архитектуру, чтобы создать их вручную.

Другие вопросы по тегам