5

我正在将 Delphi 应用程序从 Indy 9 更新到 Indy 10。

这很痛苦,因为显然发生了很多变化。

我被困在了一步。

这是旧代码(使用 Indy 9):

创建一个线程池,池中的每个线程都被初始化然后启动。各个线程创建一个 indy 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;

我在这里遇到访问冲突异常(这是 indy 代码):

procedure TIdTask.DoBeforeRun;
begin
  FBeforeRunDone := True;
  BeforeRun;
end;

FBeforeRunDone 为零。

4

1 回答 1

7

你是对的,这TIdSchedulerOfThreadPool是 Indy 10 的替代品TIdThreadMgrPool。但是,您没有考虑到的是架构与TIdScheduler架构有很大不同TIdThreadMgr

在 Indy 10 中,TIdThreadWithTask不会自行运行。顾名思义,TIdThreadWithTask执行一个Task,它是一个与线程相关联的TIdTask派生对象(例如TIdContext,它是 Indy 10 的 替代品)。TIdPeerThread您正在运行线程而不给它们执行任务,这就是您遇到崩溃的原因。为了Start()手动调用,您需要首先创建一个TIdTask基于 - 的对象并将其分配给TIdThreadWithTask.Task属性。 TIdTCPServer通过调用TIdScheduler.AcquireYarn()创建一个TIdYarn链接到对象的对象来处理它TIdThreadWithTask,然后创建一个TIdContext对象并将其传递给TIdScheduler.StartYarn(),后者使用TIdYarn访问以在调用之前TIdThreadWithTask分配其属性TaskStart()在上面。

然而,一切都没有丢失。在 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()。在 Indy 9 和 10 中,当线程停止运行并且有空间保存线程以供重用时,线程都会添加到池中,此外,仅在 Indy 10中启动TIdSchedulerOfThreadPool.NewThread()时才会将线程添加到池中。TIdTCPServer所以你实际上是在创建实际上没有做任何事情并且没有被池跟踪的线程。更有理由在两个版本中重新设计初始化代码,以便线程在正常条件下创建时自行初始化,而不是您侵入架构以手动创建它们。

于 2013-01-25T09:42:39.720 回答