如何在没有TThread的情况下从线程过程访问类的成员?

时间:2012-06-07 09:17:25

标签: multithreading delphi

我有一个像这样的单位

type
 TMyClass = Class(TObject)
private
 AnInteger      : Integer;
 MyThreadHandle : DWORD;
 procedure MyPrivateProcedure;
public
 procedure MyPublicProcedure;
end;

procedure TMyClass.MyPrivateProcedure;
 procedure MyThread; stdcall;
 begin
  if AnInteger <> 0 then MyPublicProcedure;
 end;
var
 DummyID: DWORD;
begin
  MyThreadHandle := CreateThread(NIL,0,@MyThread,NIL,0, DummyID);
end;

procedure TMyClass.MyPublicProcedure;
begin
 AnInteger := 0;
end;

我的目标是拥有一个可以“访问”变量/函数/过程的线程(不需要TT线程),就像它是类的一部分一样。此示例失败,因为它无法访问变量或过程。这只是一个例子,我知道整数不能像那样改变。对我而言,拥有一个属于该类的线程非常重要。我也尝试将整数作为指针(有效)传递给线程,但我仍然无法访问该类的过程/函数。有任何想法吗?

2 个答案:

答案 0 :(得分:6)

您可以使用TThread并保持文件大小。我认为你将走上一条艰难的道路:重新发明轮子很耗时,我可以告诉你! :)

这是一些初始化线程的工作代码:

function ThreadProc(Thread: TThread): Integer;
var FreeThread: Boolean;
begin
  if not Thread.FTerminated then
  try
    result := 0; // default ExitCode
    try
      Thread.Execute;
    except
      on Exception do
        result := -1;
    end;
  finally
    FreeThread := Thread.FFreeOnTerminate;
    Thread.FFinished := True;
    if Assigned(Thread.OnTerminate) then
      Thread.OnTerminate(Thread);
    if FreeThread then
      Thread.Free;
    EndThread(result);   
  end;
end;

constructor TThread.Create(CreateSuspended: Boolean);
begin
  IsMultiThread := true; // for FastMM4 locking, e.g.
  inherited Create;
  FSuspended := CreateSuspended;
  FCreateSuspended := CreateSuspended;
  FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID);
  if FHandle = 0 then
    raise Exception.Create(SysErrorMessage(GetLastError));
  SetThreadPriority(FHandle, THREAD_PRIORITY_NORMAL); 
end;

也就是说,将对象作为pointer()传递给线程创建API,该API将作为ThreadProc的唯一参数传递。

ThreadProc不应该是任何方法的一部分,而应该是单元的全局。

这是另一段直接调用API来处理多线程压缩的代码,没有开销和同步:

type
  TThreadParams = record
    bIn, bOut: pAESBlock;
    BlockCount: integer;
    Encrypt: boolean;
    ID: DWORD;
    AES: TAES;
  end;

{ we use direct Windows threads, since we don't need any exception handling
  nor memory usage inside the Thread handler
   -> avoid classes.TThread and system.BeginThread() use
   -> application is still "officialy" mono-threaded (i.e. IsMultiThread=false),
     for faster System.pas and FastMM4 (no locking)
   -> code is even shorter then original one using TThread }
function ThreadWrapper(var P: TThreadParams): Integer; stdcall;
begin
  with P do
    AES.DoBlocks(bIn,bOut,bIn,bOut,BlockCount,Encrypt);
  ExitThread(0);
  result := 0; // make the compiler happy, but won't never be called
end;

procedure TAES.DoBlocksThread(var bIn, bOut: PAESBlock; Count: integer; doEncrypt: boolean);
var Thread: array[0..3] of TThreadParams; // faster than dynamic array
    Handle: array[0..3] of THandle; // high(Thread) is not compiled by XE2
    nThread, i, nOne: integer;
    pIn, pOut: PAESBlock;
begin
  if Count=0 then exit;
  if {$ifdef USEPADLOCK} padlock_available or {$endif}
    (SystemInfo.dwNumberOfProcessors<=1) or // (DebugHook<>0) or
    (Count<((512*1024) div AESBlockSize)) then begin // not needed below 512 KB
    DoBlocks(bIn,bOut,bIn,bOut,Count,doEncrypt);
    exit;
  end;
  nThread := SystemInfo.dwNumberOfProcessors;
  if nThread>length(Thread) then // a quad-core is enough ;)
    nThread := length(Thread);
  nOne := Count div nThread;
  pIn := bIn;
  pOut := bOut;
  for i := 0 to nThread-1 do
  with Thread[i] do begin // create threads parameters
    bIn := pIn;
    bOut := pOut;
    BlockCount := nOne;
    Encrypt := doEncrypt;
    AES := self; // local copy of the AES context for every thread
    Handle[i] := CreateThread(nil,0,@ThreadWrapper,@Thread[i],0,ID);
    inc(pIn,nOne);
    inc(pOut,nOne);
    dec(Count,nOne);
  end;
  if Count>0 then
    DoBlocks(pIn,pOut,pIn,pOut,Count,doEncrypt); // remaining blocks
  inc(Count,nOne*nThread);
  assert(integer(pIn)-integer(bIn)=Count*AESBlockSize);
  assert(integer(pOut)-integer(bOut)=Count*AESBlockSize);
  bIn := pIn;
  bOut := pOut;
  WaitForMultipleObjects(nThread,@Handle[0],True,INFINITE); 
  for i := 0 to nThread-1 do
    CloseHandle(Handle[i]);
end;

答案 1 :(得分:5)

线程有自己的堆栈指针,因此您无法在MyThread本地过程(BTW被声明为错误)中访问局部变量或参数(如隐藏的Self参数)。此外,如果线程从外部函数访问变量(包括Self),则不能对线程使用本地过程。如果您希望将来使用64位编译器,则不能使用本地过程进行任何回调。

在您的情况下,您只需要修复过程的声明并将其移动到单位范围内(使其成为“独立”过程。这允许您使用“自我”的线程回调参数。

function MyThread(MyObj: TMyClass): DWORD; stdcall;
begin
  if MyObj.AnInteger <> 0 then
    MyObj.MyPublicProcedure;
  Result := 0;
end;

procedure TMyClass.MyPrivateProcedure;
var
  DummyID: DWORD;
begin
  MyThreadHandle := CreateThread(nil, 0, @MyThread, Self, 0, DummyID);
end;
相关问题