简体   繁体   English

使用Delphi,TIdSchedulerOfThreadPool初始化从Indy 9迁移到10

[英]Migrating from Indy 9 to 10 with Delphi, TIdSchedulerOfThreadPool initialization

I'm in the process of updating a Delphi app from Indy 9 to Indy 10. 我正在将一个Delphi应用程序从Indy 9更新到Indy 10。

It's quite painful, as apparently a lot has changed. 这很痛苦,显然已经发生了很多变化。

I'm stuck at one step. 我陷入了困境。

Here is the old code (working with Indy 9): 这是旧代码(与Indy 9一起工作):

A Thread Pool is created and every thread of the pool is initialized and then started. 创建一个线程池,初始化然后启动池的每个线程。 The individual threads create an indy http client (but it does not matter here). 各个线程创建一个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;

The TIdThreadMgrPool class is gone with Indy 10. Indy 10推出了TIdThreadMgrPool类。

I've looked for a replacement and TIdSchedulerOfThreadPool looks like a winner, but I cannot get it running. 我寻找替代品, TIdSchedulerOfThreadPool看起来像赢家,但我无法让它运行。

Here is the modified (Indy 10) code: 这是修改后的(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;

I get an access violation exception here (this is indy code): 我在这里得到一个访问冲突异常(这是indy代码):

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

FBeforeRunDone is nil. FBeforeRunDone是零。

You are correct that TIdSchedulerOfThreadPool is Indy 10's replacement for TIdThreadMgrPool . 你是对的, TIdSchedulerOfThreadPool是Indy 10替代TIdThreadMgrPool However, what you are not taking into account is that the TIdScheduler architecture is quite a bit different than the TIdThreadMgr architecture. 但是,你没有考虑到的是, TIdScheduler架构比有点不同TIdThreadMgr架构。

In Indy 10, TIdThreadWithTask does not operate by itself. 在Indy 10中, TIdThreadWithTask本身不能运行。 As its name implies, TIdThreadWithTask performs a Task , which is a TIdTask -derived object (such as TIdContext , which is Indy 10's replacement for TIdPeerThread ) that gets associated with the thread. 顾名思义, TIdThreadWithTask执行一个Task ,它是一个TIdTask派生的对象(例如TIdContext ,它是Indy 10替代TIdPeerThread ),它与线程相关联。 You are running threads without giving them tasks to perform, that is why you are experiencing crashes. 您正在运行线程而不给它们执行任务,这就是您遇到崩溃的原因。 In order to call Start() manually, you need to first create and assign a TIdTask -based object to the TIdThreadWithTask.Task property. 为了手动调用Start() ,您需要首先创建一个基于TIdTask的对象并将其分配给TIdThreadWithTask.Task属性。 TIdTCPServer handles that by calling TIdScheduler.AcquireYarn() to create a TIdYarn object that is linked to a TIdThreadWithTask object, then creates a TIdContext object and passes it to TIdScheduler.StartYarn() , which uses the TIdYarn to access the TIdThreadWithTask to assign its Task property before then calling Start() on it. TIdTCPServer处理通过调用TIdScheduler.AcquireYarn()来创建一个TIdYarn链接到一个对象TIdThreadWithTask对象,则创建一个TIdContext对象并将其传递到TIdScheduler.StartYarn()它使用TIdYarn访问TIdThreadWithTask转让其Task在之前调用Start()之前的属性。

However, all is not lost. 但是,一切都不会丢失。 In both Indy 9 and 10, you really should not be calling TIdThread.Start() manually to begin with. 在Indy 9和10中,你真的不应该手动调用TIdThread.Start()来开始。 TIdTCPServer handles that for you after accepting a new client connection, obtaining a thread from its ThreadMgr / Scheduler , and associating the client connection to the thread. TIdTCPServer在接受新的客户端连接,从其ThreadMgr / Scheduler获取线程以及将客户端连接与线程相关联后为您处理。 You can initialize your thread properties as needed without actually running the threads immediately. 您可以根据需要初始化线程属性,而无需立即实际运行线程。 The properties will take effect the first time the threads begin running at a later time. 这些属性将在线程第一次开始运行时生效。

Try this: 尝试这个:

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;

Now, with that said, one last thing to watch out for. 现在,说到这一点,最后要注意的是。 In both Indy 9 and 10, it is possible for threads to not be put back in the pool when finished, and for new threads to get added to the pool after your initialization code has run. 在Indy 9和10中,完成后线程可能不会被放回池中,并且在初始化代码运行后将新线程添加到池中。 The PoolSize is the minimum number of threads to keep in the pool, not an absolute count. PoolSize是池中保留的最小线程数,而不是绝对计数。 More than PoolSize number of clients can connect to the server and it will happily create more threads for them at the time they are needed, thus bypassing your initialization code. 不仅PoolSize数量的客户端可以连接到服务器,它将很乐意在需要时为它们创建更多线程,从而绕过初始化代码。 In both versions, the best place to initalize your threads is in the TUrlThread constructor. 在这两个版本中, TUrlThread线程的最佳位置是TUrlThread构造函数。 Store your Controler pointer somewhere that the constructor can reach it when needed. Controler指针存储在构造函数可以在需要时到达的位置。 And it does not make sense to assign an Index to each thread since the order of the threads in the pool changes dynamically over time. 由于池中线程的顺序随时间动态变化,因此为每个线程分配Index没有意义。

In fact, your manual initialization code is actually the wrong approch in both versions for another reason. 实际上,由于其他原因,您的手动初始化代码在两个版本中实际上都是错误的approch。 Both TIdThreadMgrPool.GetThread() and TIdSchedulerOfThreadPool.NewThread() do not add the new thread to the pool at all. TIdThreadMgrPool.GetThread()TIdSchedulerOfThreadPool.NewThread()都不会将新线程添加到池中。 Threads are added to the pool in both Indy 9 and 10 when a thread stops running and there is room to save the thread for reuse, and additionally in Indy 10 only when TIdTCPServer is starting up. 当线程停止运行时,线程将被添加到Indy 9和10中的池中,并且有空间可以保存线程以供重用,而且只有在TIdTCPServer启动时才在Indy 10中。 So you are actually creating threads that are not actually doing anything and are not being tracked by the pool. 因此,您实际上创建的线程实际上并没有执行任何操作,也没有被池跟踪。 All the more reason to re-design your initialization code in both versions so threads initialize themselves when they are created under normal conditions, rather than you hacking into the architecture to create them manually. 更有理由在两个版本中重新设计初始化代码,以便线程在正常条件下创建时初始化自己,而不是入侵体系结构以手动创建它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM