1

私はこのようなユニットを持っています

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;

私の目標は、クラスの一部であるかのように、変数/関数/プロシージャに「アクセス」できるスレッド(TTthreadは使用しないでください)を用意することです。この例は、変数にもプロシージャにもアクセスできないため、失敗します。これは単なる例であり、整数はそのように変更できないことを認識しています。私にとって重要なのは、クラスの一部であるスレッドを持つことです。また、整数を(機能する)ポインターとしてスレッドに渡そうとしましたが、それでもクラスのプロシージャ/関数にアクセスできません。何か案は?

4

2 に答える 2

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に関してオブジェクトを渡します。これは、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;
于 2012-06-07T09:40:44.087 に答える
5

スレッドには独自のスタックポインターがあるため、MyThreadローカルプロシージャ(ところで、間違っていると宣言されている)のローカル変数またはパラメーター(非表示のSelfパラメーターなど)にアクセスすることはできません。さらに、スレッドが外部関数から変数(Selfを含む)にアクセスする場合、スレッドにローカルプロシージャを使用することはできません。また、将来64ビットコンパイラを使用する場合は、コールバックにローカルプロシージャを使用できません。

あなたの場合、あなたはあなたのプロシージャの宣言を修正し、それをユニットスコープに移動する必要があります(それを「スタンドアロン」プロシージャにします。これにより、「Self」のスレッドコールバックパラメータを使用できます。

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;
于 2012-06-07T09:40:59.650 に答える