Как установить размер стека в TThread?

Как я могу установить пользовательский размер стека в TThread? Я пытаюсь повторно ввести конструктор TThread, но он говорит, что ThreadProc отсутствует, но находится прямо в System.Classes.

type
  TThreadHelper = class helper for TThread
    constructor Create(const CreateSuspended: Boolean = False; const StackSize: Integer = 0); reintroduce;
 end;

{ TThreadHelper }

constructor TThreadHelper.Create(const CreateSuspended: Boolean; const StackSize: Integer);
begin
  Self.FSuspended := not Self.FExternalThread;
  Self.FCreateSuspended := CreateSuspended and not Self.FExternalThread;
  if not Self.FExternalThread then
  begin
    Self.FHandle := BeginThread(nil, StackSize, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, Self.FThreadID);
    if Self.FHandle = 0 then
    raise EThread.CreateResFmt(@SThreadCreateError, [SysErrorMessage(GetLastError)]);
  end
  else
  begin
    Self.FHandle := Winapi.Windows.GetCurrentThread;
    Self.FThreadId := GetCurrentThreadId;
  end;
end;

[Ошибка dcc32] Project5.dpr(29): E2003 Необъявленный идентификатор: «ThreadProc»


person user3725897    schedule 10.06.2014    source источник
comment
Функция ThreadProc не предназначена для публичного использования. Он также был бы определен в части interface юнита. Поэтому компилятор его не видит.   -  person TLama    schedule 10.06.2014


Ответы (4)


Я не знаю, можно ли установить размер стека после создания потока. Возможно, SetThreadStackGuarantee может оказаться полезным?

Вы можете создать поток с нуля, используя BeginThread, но это довольно сложно. У меня есть обходной путь, используя Detours. Обратите внимание, что существует несколько вариантов Detours. Я думаю, что только Cromis.Detours совместим с x64.

unit IndividualStackSizeForThread;

interface

uses 
  System.Classes,
  Cromis.Detours { http://www.cromis.net/blog/downloads/cromis-ipc/ };

type
  TThreadHelper = class helper for TThread
    constructor Create(CreateSuspended: Boolean; StackSize: LongWord);
 end;

implementation

var
  TrampolineBeginThread: function(SecurityAttributes: Pointer; StackSize: LongWord;
    ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord; 
    var ThreadId: TThreadID): THandle = nil;

threadvar
  StackSizeOverride: LongWord;

function InterceptBeginThread(SecurityAttributes: Pointer; StackSize: LongWord;
  ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord;
  var ThreadId: TThreadID): THandle;
const
  STACK_SIZE_PARAM_IS_A_RESERVATION = $00010000; // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682453(v=vs.85).aspx
begin
  if StackSizeOverride <> 0 then
  begin
    CreationFlags := CreationFlags or STACK_SIZE_PARAM_IS_A_RESERVATION;
    StackSize := StackSizeOverride;
    StackSizeOverride := 0;
  end;

  Result := TrampolineBeginThread(SecurityAttributes, StackSize, ThreadFunc, 
    Parameter, CreationFlags, ThreadId);
end;

constructor TThreadHelper.Create(CreateSuspended: Boolean; StackSize: LongWord);
begin
  StackSizeOverride := StackSize;
  inherited Create(CreateSuspended);
end;

initialization

TrampolineBeginThread := InterceptCreate(@BeginThread, @InterceptBeginThread);

finalization

InterceptRemove(@TrampolineBeginThread, @InterceptBeginThread);

end.

Я не знаю, почему Embt не позволяет программисту указать размер стека, если кто-то знает причину, мне будет очень интересно.

person stanleyxu2005    schedule 10.06.2014

Просто нет возможности контролировать размер стека с помощью TThread. По какой-то причине разработчики TThread не смогли включить параметр размера стека в конструктор TThread. Это явное упущение. Вы должны позвонить BeginThread или CreateThread напрямую.

Если вы просто отчаянно хотите, чтобы ваш хак работал, вам нужно найти адрес функции ThreadProc, объявленной в разделе реализации модуля Classes. Некоторые возможные подходы:

  1. Разберите TThread.Create во время выполнения, чтобы прочитать адрес ThreadProc.
  2. Создайте фиктивный поток, который просматривает свой стек вызовов, чтобы найти адрес ThreadProc.
  3. Крюк BeginThread объездным путем. Создайте фиктивную ветку. Обратите внимание на адрес переданной процедуры потока. Это ThreadProc.

Хорошим источником идей для такого рода взлома является исходный код madExcept.

Еще один способ применить хак — снова использовать объезд на BeginThread. Затем вы можете использовать локальную переменную потока для указания размера стека. Значение high(LongWord) для этой локальной переменной потока будет означать "использовать значение, переданное в качестве параметра", а любое другое значение будет значением, используемым обходным BeginThread.

person David Heffernan    schedule 10.06.2014
comment
Вы можете использовать любую библиотеку обхода, которая вам нравится - person David Heffernan; 10.06.2014

Как указывает Дэвид, вы не можете контролировать размер стека для потоков, созданных с помощью класса TThread. Вам придется создать фактический поток самостоятельно, используя либо BeginThread, либо CreateThread.

Однако, если вам не нужны разные размеры стека для каждого потока в вашем приложении:
тогда вы можете установить размер стека по умолчанию, используя параметры связывания для Минимальный размер стека и Максимальный размер стека. .

Либо установите параметр в настройках проекта, либо используйте директивы: {$M minstacksize,maxstacksize} {$MINSTACKSIZE number} {$MAXSTACKSIZE number}

person Disillusioned    schedule 10.06.2014

Для Delphi 10.3 RIO и более поздних версий теперь имеется перегруженный конструктор, который позволяет вам устанавливать ReservedStackSize для TThread на платформе Windows.

{$IF Defined(MSWINDOWS)}
    constructor Create(CreateSuspended: Boolean; ReservedStackSize: NativeUInt); overload;
{$ENDIF MSWINDOWS}
person Darian Miller    schedule 08.05.2020