📄 jvmtthreading.pas
字号:
{-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: MTThreading.pas, released on 2000-09-22.
The Initial Developer of the Original Code is Erwin Molendijk.
Portions created by Erwin Molendijk are Copyright (C) 2002 Erwin Molendijk.
All Rights Reserved.
Contributor(s): ______________________________________.
You may retrieve the latest version of this file at the Project JEDI home page,
located at http://www.delphi-jedi.org
Known Issues:
-----------------------------------------------------------------------------}
// $Id: JvMTThreading.pas,v 1.33 2005/03/09 14:57:27 marquardt Exp $
unit JvMTThreading;
{$I jvcl.inc}
interface
uses
{$IFDEF USEJVCL}
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
{$ENDIF USEJVCL}
SysUtils, Classes, SyncObjs, Contnrs,
{$IFDEF MSWINDOWS}
Windows, Messages,
{$ENDIF MSWINDOWS}
{$IFDEF HAS_UNIT_LIBC}
Libc,
{$ENDIF HAS_UNIT_LIBC}
{$IFDEF UNIX}
QWindows,
{$ENDIF UNIX}
{$IFDEF COMPILER5}
Forms,
{$ENDIF COMPILER5}
JvMTConsts, JvMTSync;
type
TMTManager = class;
TMTThread = class;
TMTEvent = procedure(Thread: TMTThread) of object;
{$IFDEF COMPILER5}
TIntThread = class(Classes.TThread)
public
destructor Destroy; override;
procedure Synchronize(Method: TThreadMethod);
function WaitFor: Longword;
end;
{$ELSE}
TIntThread = TThread;
{$ENDIF COMPILER5}
TMTInternalThread = class(TIntThread)
private
FName: string;
FOnExecute: TNotifyEvent;
protected
procedure Execute; override;
procedure RaiseName;
public
property Name: string read FName write FName;
property OnExecute: TNotifyEvent read FOnExecute write FOnExecute;
end;
TMTThread = class(TObject)
private
FFinished: Boolean;
FIntThread: TMTInternalThread;
FManager: TMTManager;
FName: string;
FOnExecute: TMTEvent;
FOnFinished: TMTEvent;
FOnTerminating: TMTEvent;
FReferenceCount: Integer;
FStatusChange: TCriticalSection;
FTerminateSignal: THandle;
FTicket: TMTTicket;
procedure CreateAndRun;
function GetStatus: TMTThreadStatus;
procedure Log(const Msg: string);
procedure OnIntThreadExecute(Sender: TObject);
procedure OnIntThreadTerminate(Sender: TObject);
{$IFDEF COMPILER5}
procedure SyncOnIntThreadTerminate;
{$ENDIF COMPILER5}
procedure SetName(const Value: string);
protected
procedure DecRef;
procedure IncRef;
public
constructor Create(Manager: TMTManager; Ticket: Integer);
destructor Destroy; override;
procedure CheckTerminate;
procedure Release;
procedure Run;
procedure Synchronize(Method: TThreadMethod);
procedure Terminate;
procedure Wait;
property Name: string read FName write SetName;
property OnExecute: TMTEvent read FOnExecute write FOnExecute;
property OnFinished: TMTEvent read FOnFinished write FOnFinished;
property OnTerminating: TMTEvent read FOnTerminating write FOnTerminating;
property ReferenceCount: Integer read FReferenceCount;
property Status: TMTThreadStatus read GetStatus;
property TerminateSignal: THandle read FTerminateSignal;
property ThreadManager: TMTManager read FManager;
property Ticket: TMTTicket read FTicket;
end;
TMTManager = class(TObject)
private
FGenTicket: TCriticalSection;
FNextTicket: TMTTicket;
FThreads: TObjectList;
FThreadsChange: TCriticalSection;
function FindThread(Ticket: TMTTicket; var Thread: TMTThread): Boolean;
function GenerateTicket: TMTTicket;
procedure Log(const Msg: string);
procedure TryRemoveThread(Thread: TMTThread);
function InternalActiveThreads(RaiseID: Longword): Integer;
protected
procedure OnThreadFinished(Thread: TMTThread);
public
constructor Create;
destructor Destroy; override;
function AcquireNewThread: TMTThread;
function AcquireThread(Ticket: TMTTicket; var Thread: TMTThread): Boolean;
function ActiveThreads: Boolean;
procedure ReleaseThread(Ticket: TMTTicket);
procedure TerminateThreads;
procedure WaitThreads;
end;
function CurrentMTThread: TMTThread;
{$IFDEF USEJVCL}
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$RCSfile: JvMTThreading.pas,v $';
Revision: '$Revision: 1.33 $';
Date: '$Date: 2005/03/09 14:57:27 $';
LogPath: 'JVCL\run'
);
{$ENDIF UNITVERSIONING}
{$ENDIF USEJVCL}
implementation
{$IFDEF USEJVCL}
uses
JvResources;
{$ENDIF USEJVCL}
{$IFNDEF USEJVCL}
resourcestring
RsECurThreadIsPartOfManager = 'Current MTThread is part of the MTManager';
RsECheckTerminateCalledByWrongThread = 'CheckTerminate can only be called by the same thread';
RsEThreadNotInitializedOrWaiting = 'Cannot run: thread is not Initializing or Waiting';
RsECannotChangeNameOfOtherActiveThread = 'Cannot change name of other active thread';
RsEReleaseOfUnusedTicket = 'Release of unused ticket';
{$ENDIF !USEJVCL}
threadvar
_CurrentMTThread: TMTThread;
function CurrentMTThread: TMTThread;
begin
Result := _CurrentMTThread;
end;
{$IFDEF COMPILER5}
type
PSyncRequest = ^TSyncRequest;
TSyncRequest = record
Method: TThreadMethod;
ExceptionObject: TObject;
Signal: THandle;
end;
var
SyncRequestAvailable: Boolean;
ThreadSyncLock: TRTLCriticalSection;
SyncRequestList: TList = nil;
SyncWindow: HWND;
function CheckSynchronize: Boolean;
var
SyncRequest: PSyncRequest;
begin
Result := False;
// Only the main thread is allowed to synchronize thread methods.
if GetCurrentThreadID <> MainThreadID then
Exit;
EnterCriticalSection(ThreadSyncLock);
try
if SyncRequestAvailable and (SyncRequestList <> nil) then
begin
// Do not block while another thread is adding a new synchronization request.
while SyncRequestList.Count > 0 do
begin
SyncRequest := SyncRequestList[0];
SyncRequestList.Delete(0);
try
SyncRequest.Method;
except
SyncRequest^.ExceptionObject := ExceptObject;
end;
// inform TIntThread.Synchronize
SetEvent(SyncRequest.Signal);
SyncRequestAvailable := False;
Result := True;
end;
end;
finally
LeaveCriticalSection(ThreadSyncLock);
end;
end;
procedure FinalizeSyncRequestList;
begin
// if the list is not empty there are still waiting threads
if SyncRequestList <> nil then
begin
CheckSynchronize;
SyncRequestList.Free;
SyncRequestList := nil;
end;
end;
function SyncWndProc(wnd: HWND; Msg: Cardinal; wParam, lParam: Integer): Integer; stdcall;
begin
if Msg = WM_USER + 1 then
Result := Integer(CheckSynchronize)
else
Result := DefWindowProc(wnd, Msg, wParam, lParam);
end;
//=== { TIntThread } =========================================================
procedure TIntThread.Synchronize(Method: TThreadMethod);
var
SyncRequest: TSyncRequest;
begin
if GetCurrentThreadID = MainThreadID then
Method
else
begin
SyncRequest.Signal := CreateEvent(nil, True, False, nil);
try
EnterCriticalSection(ThreadSyncLock);
try
if SyncRequestList = nil then
SyncRequestList := TList.Create;
SyncRequest.ExceptionObject := nil;
SyncRequest.Method := Method;
// The function returns only when the item is deleted from the List.
SyncRequestList.Add(@SyncRequest);
SyncRequestAvailable := True;
finally
LeaveCriticalSection(ThreadSyncLock);
end;
PostMessage(SyncWindow, WM_USER + 1, 0, 0);
// Wait for CheckSynchronize.
WaitForSingleObject(SyncRequest.Signal, INFINITE);
finally
CloseHandle(SyncRequest.Signal);
end;
// An exception occured. Re-raise it in the calling thread's context.
if Assigned(SyncRequest.ExceptionObject) then
raise SyncRequest.ExceptionObject;
end;
end;
destructor TIntThread.Destroy;
begin
CheckSynchronize;
inherited Destroy;
end;
function TIntThread.WaitFor: Longword;
begin
CheckSynchronize;
Result := inherited WaitFor;
end;
{$ENDIF COMPILER5}
//=== { TMTInternalThread } ==================================================
procedure TMTInternalThread.Execute;
begin
RaiseName;
if Assigned(FOnExecute) then
FOnExecute(Self);
end;
procedure TMTInternalThread.RaiseName;
{$IFDEF COMPILER7_UP}
var
ThreadNameInfo: TThreadNameInfo;
{$ENDIF COMPILER7_UP}
begin
{$IFDEF COMPILER7_UP}
ThreadNameInfo.FType := $1000;
ThreadNameInfo.FName := PChar(FName);
ThreadNameInfo.FThreadID := $FFFFFFFF;
ThreadNameInfo.FFlags := 0;
try
RaiseException($406D1388, 0, SizeOf(ThreadNameInfo) div SizeOf(Longword),
@ThreadNameInfo);
except
end;
{$ENDIF COMPILER7_UP}
end;
//=== { TMTThread } ==========================================================
constructor TMTThread.Create(Manager: TMTManager; Ticket: Integer);
begin
inherited Create;
FStatusChange := TCriticalSection.Create;
FManager := Manager;
FTicket := Ticket;
FName := 'MT' + IntToStr(Ticket); // do not localize
FTerminateSignal := CreateSemaphore(nil, 0, 1, '');
end;
destructor TMTThread.Destroy;
begin
CloseHandle(FTerminateSignal);
FStatusChange.Free;
inherited Destroy;
end;
procedure TMTThread.CheckTerminate;
begin
if CurrentMTThread <> Self then
raise EMTThreadError.CreateRes(@RsECheckTerminateCalledByWrongThread);
if Status = tsTerminating then
raise EMTTerminateError.Create('');
end;
procedure TMTThread.CreateAndRun;
begin
FStatusChange.Acquire;
try
FIntThread := TMTInternalThread.Create(True);
FIntThread.OnExecute := OnIntThreadExecute;
FIntThread.OnTerminate := OnIntThreadTerminate;
FIntThread.FreeOnTerminate := True;
FIntThread.Name := FName;
FIntThread.Resume;
finally
FStatusChange.Release;
end;
end;
procedure TMTThread.DecRef;
begin
InterlockedDecrement(FReferenceCount);
end;
function TMTThread.GetStatus: TMTThreadStatus;
begin
FStatusChange.Acquire;
try
if FFinished then
Result := tsFinished
else
if FIntThread = nil then
Result := tsInitializing
else
if FIntThread.Suspended then
Result := tsWaiting
else
if FIntThread.Terminated then
Result := tsTerminating
else
Result := tsRunning;
finally
FStatusChange.Release;
end;
end;
procedure TMTThread.IncRef;
begin
InterlockedIncrement(FReferenceCount);
end;
procedure TMTThread.Log(const Msg: string);
begin
// (rom) no OutputDebugString in production code
{$IFDEF DEBUGINFO_ON}
OutputDebugString(PChar('[' + ClassName + '] ' + Msg));
{$ENDIF DEBUGINFO_ON}
end;
procedure TMTThread.OnIntThreadExecute(Sender: TObject);
begin
// set the CurrentMTThread variable.
// this variable is global, but only to this thread.
_CurrentMTThread := Self;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -