Delphi 在挂起模式下创建的线程在 windows 2012 服务器上自行启动
Delphi thread created in suspended mode gets started by itself on windows 2012 server
过去几天我一直在为一个我无法理解的错误而苦苦挣扎。
这只发生在 Windows 2012 服务器(64 位),而它不会发生(至少)在以下 windows 版本:Windows XP(32 位),Windows 7(32 和 64 位),Windows 8(64 位),Windows 8.1(64 位),Windows 10(64 位), Windows 2003 服务器(32 位)。
请注意,所有情况下的应用程序都是 32 位二进制文件。
当应用程序启动时,它会初始化一些东西,最后它会创建一系列线程,所有线程都处于挂起模式,以便我稍后可以手动启动它们。
在下面的代码中,人们会期望 t 在睡眠后启动。
然而,当我试图在线程上显式调用 Start 时,我意识到它已经启动(不知何故),导致 thread already started 异常(考虑到线程已经启动,这没问题)。
// some initialization code is run before this, but nothing directly related
procedure foo;
var
t : TThread; // note that there can be no reference to this thread from the outside since it's a local var
begin
t := TMyThread.Create(true); // TMyThread
sleep(3000); // time window of 3 seconds where the thread shouldn't have started
t.Start; // now i want to start it manually, but the thread has already been started soon after it has been created
end;
常规(虚拟)匿名线程也会发生同样的情况,所以这不是 TMyThread 的错:
// some initialization code is run before this, but nothing directly related
procedure foo;
var
t : TThread; // note that there can be no reference to this thread from the outside since it's a local var
begin
t := TThread.CreateAnonymousThread( // to avoid using my possibly faulted TMyThread class, I'm now testing with a regular anonymous thread and the problem persists
procedure begin
log('Hi from anon thread');
sleep(5000); // to make it live 5 seconds for testing purposes
end);
sleep(3000); // time window of 3 seconds where the thread shouldn't have started
t.Start; // now i want to start it manually, but the thread has already been started soon after it has been created
end;
Windows 2012 服务器中是否存在一些我缺少的关于线程管理的机制?
日志摘录:
以下是健康日志文件的摘录。您可以看到 start 方法如何调用 startThreads 方法,后者又创建了第一个线程。下一行表示从 CMP_AbstractThread 的构造函数进行的调用。 evInitialized 和 evStarted 是在 CMP_AbstractThread 的构造函数中创建的 TEvent。
12/03/2016 20:28:47.336: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMPPS_Application.start: start ...
12/03/2016 20:28:47.336: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMPPS_Application.createThreads: createThreads ...
12/03/2016 20:28:47.352: LOG_FINEST @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=0 (CMPP_EventsThread)).Create ...
12/03/2016 20:28:47.352: LOG_FINEST @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread)).Create.
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evInitialized).Create ...
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evInitialized).Create.
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evInitialized).resetEvent ...
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evInitialized).resetEvent.
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evStarted).Create ...
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evStarted).Create.
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evStarted).resetEvent ...
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evStarted).resetEvent.
以下摘自 Windows 2012 session。您可以看到线程甚至在它自己的构造函数完成之前就启动了。来自线程 Execute 方法的日志行来自线程 5864,该线程仍在创建中。在这种特定情况下,异常甚至没有等待构造函数完成(它通常会这样做)。似乎 TThread 是使用 False 而不是 True 创建的。
12/03/2016 20:29:31.813: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMPPS_Application.start: start ...
12/03/2016 20:29:31.813: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMPPS_Application.createThreads: createThreads ...
12/03/2016 20:29:31.813: LOG_FINEST @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=0 (CMPP_EventsThread)).Create ...
12/03/2016 20:29:31.813: LOG_FINEST @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread)).Create.
12/03/2016 20:29:31.813: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evInitialized).Create ...
12/03/2016 20:29:31.813: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evInitialized).Create.
12/03/2016 20:29:31.813: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evInitialized).resetEvent ...
12/03/2016 20:29:31.813: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evInitialized).resetEvent.
12/03/2016 20:29:31.813: LOG_FINEST @ PID=3796 ThreadID=5864 (CMPP_EventsThread) @ CMP_AbstractThread.Execute: (PID=3796 ThreadID=5864 (CMPP_EventsThread)) Executed.
12/03/2016 20:29:31.813: LOG_NORMAL @ PID=3796 ThreadID=5864 (CMPP_EventsThread) @ CMP_AbstractThread.Execute: (PID=3796 ThreadID=5864 (CMPP_EventsThread)) Down.
12/03/2016 20:29:31.828: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evStarted).Create ...
12/03/2016 20:29:31.828: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evStarted).Create.
12/03/2016 20:29:31.828: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evStarted).resetEvent ...
12/03/2016 20:29:31.828: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evStarted).resetEvent.
代码摘录:
constructor CMP_AbstractThread.Create(suspended : boolean);
begin
inherited Create(suspended); // this one sets up threadName
FEvInitialized := CMP_Event.Create(threadName, 'evInitialized');
FEvInitialized.ResetEvent;
FEvStarted := CMP_Event.Create(threadName, 'evStarted');
FEvStarted.ResetEvent;
end;
Constructor CMP_Thread.Create(suspended : boolean);
Begin
log(LOG_FINEST, format('(%s).Create ...', [ThreadInfo]));
inherited Create(suspended);
FThreadName := threadInfo;
log(LOG_FINEST, format('(%s).Create.', [ThreadName]));
End;
procedure CMPPS_Application.createThreads;
begin
logGuard('createThreads', procedure begin
FEventsThread := CMPP_EventsThread(CMPP_EventsThread.Create(true)
.setDbConnection(FDB)
.setLogger(GEventsLogger)
.setDelay(MP_EVENTS_THREAD_DELAY));
end);
end;
CMPP_EventsThread = class(CMPC_EventsThread)
// all the following methods are inherited from different layers
// Create(boolean) is inherited directly from CMP_AbstractThread
// setDbConnection(IMP_DBConnection)
// setLogger(IMP_Logger)
// setDelay(integer)
end;
你所描述的在正常情况下是不可能的。如果 TThread
构造函数的 ACreateSuspended
参数为 True,则创建的线程处于挂起状态。没有如果,和,或关于那个。挂起的线程无法自发启动。
匿名线程总是被挂起。
假设您在有效的 TThread
对象上调用 TThread.Start()
,Start()
引发异常 仅 如果出现以下任一情况:
FCreateSuspended
成员为 False。如果 ACreateSuspended
参数为 True,则此成员在 TThread
构造函数中设置为 True,并由 Start()
设置为 False。您只能调用 Start()
一次。
FFinished
成员为真。这在调用 TThread.Execute()
和 TThread.DoTerminate()
后设置为 True,这意味着线程已经 运行 完成。您不能在已终止的线程上调用 Start()
。
FExternalThread
成员为真。这是为 TThread.GetCurrentThread()
返回的 TThread
对象设置的。您不能在未明确 Create()
.
的线程上调用 Start()
以上条件都OK,但是底层OS线程(由TThread.Handle
表示属性)没有成功从挂起状态转换到运行 状态。 仅 可能发生的方式是,如果您的代码中的某些内容正在调用 TThread.Suspend()
或 TThread.Resume()
(或者更糟,某些内容正在调用 Win32 API SuspendThread()
or ResumeThread()
直接)在调用 TThread.Start()
.
之前更改线程的挂起计数
鉴于您显示的示例代码,这些条件是不可能的。所以问题必须与您没有显示的代码有关。
过去几天我一直在为一个我无法理解的错误而苦苦挣扎。 这只发生在 Windows 2012 服务器(64 位),而它不会发生(至少)在以下 windows 版本:Windows XP(32 位),Windows 7(32 和 64 位),Windows 8(64 位),Windows 8.1(64 位),Windows 10(64 位), Windows 2003 服务器(32 位)。
请注意,所有情况下的应用程序都是 32 位二进制文件。
当应用程序启动时,它会初始化一些东西,最后它会创建一系列线程,所有线程都处于挂起模式,以便我稍后可以手动启动它们。
在下面的代码中,人们会期望 t 在睡眠后启动。 然而,当我试图在线程上显式调用 Start 时,我意识到它已经启动(不知何故),导致 thread already started 异常(考虑到线程已经启动,这没问题)。
// some initialization code is run before this, but nothing directly related
procedure foo;
var
t : TThread; // note that there can be no reference to this thread from the outside since it's a local var
begin
t := TMyThread.Create(true); // TMyThread
sleep(3000); // time window of 3 seconds where the thread shouldn't have started
t.Start; // now i want to start it manually, but the thread has already been started soon after it has been created
end;
常规(虚拟)匿名线程也会发生同样的情况,所以这不是 TMyThread 的错:
// some initialization code is run before this, but nothing directly related
procedure foo;
var
t : TThread; // note that there can be no reference to this thread from the outside since it's a local var
begin
t := TThread.CreateAnonymousThread( // to avoid using my possibly faulted TMyThread class, I'm now testing with a regular anonymous thread and the problem persists
procedure begin
log('Hi from anon thread');
sleep(5000); // to make it live 5 seconds for testing purposes
end);
sleep(3000); // time window of 3 seconds where the thread shouldn't have started
t.Start; // now i want to start it manually, but the thread has already been started soon after it has been created
end;
Windows 2012 服务器中是否存在一些我缺少的关于线程管理的机制?
日志摘录:
以下是健康日志文件的摘录。您可以看到 start 方法如何调用 startThreads 方法,后者又创建了第一个线程。下一行表示从 CMP_AbstractThread 的构造函数进行的调用。 evInitialized 和 evStarted 是在 CMP_AbstractThread 的构造函数中创建的 TEvent。
12/03/2016 20:28:47.336: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMPPS_Application.start: start ...
12/03/2016 20:28:47.336: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMPPS_Application.createThreads: createThreads ...
12/03/2016 20:28:47.352: LOG_FINEST @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=0 (CMPP_EventsThread)).Create ...
12/03/2016 20:28:47.352: LOG_FINEST @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread)).Create.
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evInitialized).Create ...
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evInitialized).Create.
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evInitialized).resetEvent ...
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evInitialized).resetEvent.
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evStarted).Create ...
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evStarted).Create.
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evStarted).resetEvent ...
12/03/2016 20:28:47.352: LOG_DEBUG @ PID=5576 ThreadID=5188 (TExternalThread) @ CMP_AbstractThread.: (PID=5576 ThreadID=6084 (CMPP_EventsThread).evStarted).resetEvent.
以下摘自 Windows 2012 session。您可以看到线程甚至在它自己的构造函数完成之前就启动了。来自线程 Execute 方法的日志行来自线程 5864,该线程仍在创建中。在这种特定情况下,异常甚至没有等待构造函数完成(它通常会这样做)。似乎 TThread 是使用 False 而不是 True 创建的。
12/03/2016 20:29:31.813: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMPPS_Application.start: start ...
12/03/2016 20:29:31.813: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMPPS_Application.createThreads: createThreads ...
12/03/2016 20:29:31.813: LOG_FINEST @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=0 (CMPP_EventsThread)).Create ...
12/03/2016 20:29:31.813: LOG_FINEST @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread)).Create.
12/03/2016 20:29:31.813: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evInitialized).Create ...
12/03/2016 20:29:31.813: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evInitialized).Create.
12/03/2016 20:29:31.813: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evInitialized).resetEvent ...
12/03/2016 20:29:31.813: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evInitialized).resetEvent.
12/03/2016 20:29:31.813: LOG_FINEST @ PID=3796 ThreadID=5864 (CMPP_EventsThread) @ CMP_AbstractThread.Execute: (PID=3796 ThreadID=5864 (CMPP_EventsThread)) Executed.
12/03/2016 20:29:31.813: LOG_NORMAL @ PID=3796 ThreadID=5864 (CMPP_EventsThread) @ CMP_AbstractThread.Execute: (PID=3796 ThreadID=5864 (CMPP_EventsThread)) Down.
12/03/2016 20:29:31.828: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evStarted).Create ...
12/03/2016 20:29:31.828: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evStarted).Create.
12/03/2016 20:29:31.828: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evStarted).resetEvent ...
12/03/2016 20:29:31.828: LOG_DEBUG @ PID=3796 ThreadID=4460 (TExternalThread) @ CMP_AbstractThread.: (PID=3796 ThreadID=5864 (CMPP_EventsThread).evStarted).resetEvent.
代码摘录:
constructor CMP_AbstractThread.Create(suspended : boolean);
begin
inherited Create(suspended); // this one sets up threadName
FEvInitialized := CMP_Event.Create(threadName, 'evInitialized');
FEvInitialized.ResetEvent;
FEvStarted := CMP_Event.Create(threadName, 'evStarted');
FEvStarted.ResetEvent;
end;
Constructor CMP_Thread.Create(suspended : boolean);
Begin
log(LOG_FINEST, format('(%s).Create ...', [ThreadInfo]));
inherited Create(suspended);
FThreadName := threadInfo;
log(LOG_FINEST, format('(%s).Create.', [ThreadName]));
End;
procedure CMPPS_Application.createThreads;
begin
logGuard('createThreads', procedure begin
FEventsThread := CMPP_EventsThread(CMPP_EventsThread.Create(true)
.setDbConnection(FDB)
.setLogger(GEventsLogger)
.setDelay(MP_EVENTS_THREAD_DELAY));
end);
end;
CMPP_EventsThread = class(CMPC_EventsThread)
// all the following methods are inherited from different layers
// Create(boolean) is inherited directly from CMP_AbstractThread
// setDbConnection(IMP_DBConnection)
// setLogger(IMP_Logger)
// setDelay(integer)
end;
你所描述的在正常情况下是不可能的。如果 TThread
构造函数的 ACreateSuspended
参数为 True,则创建的线程处于挂起状态。没有如果,和,或关于那个。挂起的线程无法自发启动。
匿名线程总是被挂起。
假设您在有效的 TThread
对象上调用 TThread.Start()
,Start()
引发异常 仅 如果出现以下任一情况:
FCreateSuspended
成员为 False。如果ACreateSuspended
参数为 True,则此成员在TThread
构造函数中设置为 True,并由Start()
设置为 False。您只能调用Start()
一次。FFinished
成员为真。这在调用TThread.Execute()
和TThread.DoTerminate()
后设置为 True,这意味着线程已经 运行 完成。您不能在已终止的线程上调用Start()
。FExternalThread
成员为真。这是为TThread.GetCurrentThread()
返回的TThread
对象设置的。您不能在未明确Create()
. 的线程上调用 以上条件都OK,但是底层OS线程(由
TThread.Handle
表示属性)没有成功从挂起状态转换到运行 状态。 仅 可能发生的方式是,如果您的代码中的某些内容正在调用TThread.Suspend()
或TThread.Resume()
(或者更糟,某些内容正在调用 Win32 APISuspendThread()
orResumeThread()
直接)在调用TThread.Start()
. 之前更改线程的挂起计数
Start()
鉴于您显示的示例代码,这些条件是不可能的。所以问题必须与您没有显示的代码有关。