Multithreading 在挂起模式下创建的Delphi线程在windows 2012服务器上自行启动

Multithreading 在挂起模式下创建的Delphi线程在windows 2012服务器上自行启动,multithreading,delphi,delphi-xe,windows-server-2012,delphi-10-seattle,Multithreading,Delphi,Delphi Xe,Windows Server 2012,Delphi 10 Seattle,过去几天我一直在为一个我不理解的bug而挣扎。 这仅在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位二进制文件 当应用程序启动时,它会初始化一些东西,最后它会创建一系列线程,所有线程都处于挂起模式,以便我以

过去几天我一直在为一个我不理解的bug而挣扎。 这仅在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时,我意识到它已经启动了(不知何故),导致了一个线程已经启动异常(考虑到线程已经启动,这是可以的)

常规(虚拟)匿名线程也会发生同样的情况,因此这不是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 server中是否存在我所缺少的线程管理机制

日志摘录

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;
以下是一个健康日志文件的摘录。您可以看到start方法如何调用startThreads方法,后者依次创建第一个线程。接下来的几行表示从CMP_AbstractThread的构造函数发出的调用。evInitialized和evStarted是在CMP_AbstractThread的构造函数中创建的事件

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会话的摘录。您可以看到线程甚至在自己的构造函数完成之前就启动了。来自threads 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
    成员为True。在调用
    TThread.Execute()
    TThread.DoTerminate()
    后,这将设置为True,这意味着线程一直在运行到完成。不能在终止的线程上调用
    Start()

  • FExternalThread
    成员为True。这是为
    TThread.GetCurrentThread()
    返回的
    TThread
    对象设置的。不能在未显式创建()的线程上调用
    Start()

  • 上述条件正常,但底层OS线程(由
    TThread.Handle
    属性表示)未成功从挂起状态转换为运行状态。唯一可能发生的方法是,如果代码中的某些内容正在调用
    TThread.Suspend()
    TThread.Resume()
    (或者更糟糕的是,某些内容正在调用Win32 API或直接调用),以在调用
    TThread.Start()
    之前更改线程的挂起计数


  • 根据您展示的示例代码,这些条件是不可能的。因此,问题必须与您未显示的代码有关。

    您所描述的内容在正常情况下是不可能的。如果
    TThread
    构造函数的
    ACreateSuspended
    参数为True,则线程将在挂起状态下创建。关于那件事,没有如果,和,但是。挂起的线程无法自动启动

    匿名线程的创建总是挂起的

    假设您正在对有效的
    TThread
    对象调用
    TThread.Start()
    Start()
    仅在以下情况下引发异常:

  • FCreateSuspended
    成员为False。如果
    ACreateSuspended
    参数为True,则在
    TThread
    构造函数中将此成员设置为True,并通过
    Start()
    将其设置为False。只能调用一次
    Start()

  • FFinished
    成员为True。在调用
    TThread.Execute()
    TThread.DoTerminate()
    后,这将设置为True,这意味着线程一直在运行到完成。不能在终止的线程上调用
    Start()

  • FExternalThread
    成员为True。这是为
    TThread.GetCurrentThread()
    返回的
    TThread
    对象设置的。不能在未显式创建()的线程上调用
    Start()

  • 上述条件正常,但底层OS线程(由
    TThread.Handle
    属性表示)未成功从挂起状态转换为运行状态。唯一可能发生的方法是,如果代码中的某些内容正在调用
    TThread.Suspend()
    TThread.Resume()
    (或者更糟的是,某些内容正在调用Win32 API或直接调用)来更改线程的挂起计数