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

Edu*_*rdo 1 delphi multithreading delphi-xe windows-server-2012 delphi-10-seattle

在过去的几天里,我一直在努力解决一个我无法理解的错误.这仅发生在Windows 2012服务器(64位)中,而在以下Windows版本中不会发生(至少):Windows XP(32位),Windows 7(32位和64位),Windows 8(64位),Windows 8.1 (64位),Windows 10(64位),Windows 2003 Server(32位).

请注意,在所有情况下,应用程序都是32位二进制文​​件.

当应用程序启动时,它初始化一些东西,最后它创建了一系列线程,所有这些线程都处于挂起模式,以便我可以在以后手动启动它们.

在下面的代码中,人们期望在睡眠之后启动t.但是,当我尝试在线程上显式调用Start时,我意识到它已经启动(不知何故),导致线程已经启动异常(考虑到线程已经启动,这将是正常的).

// 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;
Run Code Online (Sandbox Code Playgroud)

对于常规(虚拟)匿名线程也会发生同样的情况,因此它不会是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;
Run Code Online (Sandbox Code Playgroud)

Windows 2012服务器中是否存在一些我缺少的线程管理机制?

日志EXCERPTS:

以下是健康日志文件的摘录.您可以看到start方法如何调用startThreads方法,该方法依次创建第一个线程.下一行代表从CMP_AbstractThread的构造函数调用.evInitialized和evStarted是在CMP_AbstractThread的构造函数中创建的TEvents.

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.
Run Code Online (Sandbox Code Playgroud)

以下是Windows 2012会话的摘录.你可以看到线程甚至在它自己的构造函数完成之前就开始了.来自线程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.
Run Code Online (Sandbox Code Playgroud)

代码EXCERPTS:

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;
Run Code Online (Sandbox Code Playgroud)

Rem*_*eau 5

在正常情况下,你所描述的是相当不可能的.如果构造函数的ACreateSuspended参数TThread为True,则以挂起状态创建线程.没有ifs,ands或buts.悬浮的线程不能自发地开始.

始终创建匿名线程暂停.

假设您正在调用TThread.Start()有效TThread对象,则在以下Start()情况之一时引发异常:

  1. FCreateSuspended成员是假的.TThread如果ACreateSuspended参数为True,则此成员在构造函数中设置为True,并设置为False by Start().你只能打Start()一次电话.

  2. FFinished成员是真的.这是设置为True后TThread.Execute(),并TThread.DoTerminate()一直呼吁,表示线程已经运行至完成.您无法调用Start()已终止的线程.

  3. FExternalThread成员是真的.这是为TThread返回的对象设置的TThread.GetCurrentThread().你不能调用Start()你没有明确的线程Create().

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

鉴于您显示的示例代码,这些条件是不可能的.所以问题必须与你没有展示的代码有关.