[英]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.