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