我正在将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;

Indy 10不再使用 TIdThreadMgrPool 类。

我一直在寻找替代品,而 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为零。

最佳答案

您认为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来分配其Task属性,然后再对其调用Start()

但是,一切并没有丢失。在Indy 9和Indy 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和Indy 10中,有可能在完成后不将线程放回池中,并且有可能在运行初始化代码后将新线程添加到池中。 PoolSize是要保留在池中的最小线程数,而不是绝对计数。可以连接到服务器的客户端数量超过PoolSize,它会在需要它们的时候愉快地为他们创建更多的线程,从而绕过您的初始化代码。在这两个版本中,初始化线程的最佳位置是TUrlThread构造函数。将Controler指针存储在构造函数可以在需要时到达的位置。而且,为每个线程分配一个Index没有意义,因为池中线程的顺序会随着时间动态变化。

实际上,由于另一个原因,您的手动初始化代码实际上在两个版本中都是错误的方法。 TIdThreadMgrPool.GetThread()TIdSchedulerOfThreadPool.NewThread()都根本不将新线程添加到池中。当线程停止运行并且有空间保存线程以供重用时,将线程添加到Indy 9和10中的线程池中;此外,仅当TIdTCPServer启动时,才在Indy 10中将线程添加到池中。因此,您实际上正在创建的线程实际上并没有执行任何操作,也没有被池跟踪。还有更多理由重新设计这两个版本中的初始化代码,以便在正常条件下创建线程时可以对线程进行初始化,而不必亲自干预体系结构以手动创建它们。

关于delphi - 使用Delphi,TIdSchedulerOfThreadPool初始化从Indy 9迁移到10,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14514810/

10-15 13:41