多线程编程

来源:互联网 发布:网络爆笑儿童喜剧 编辑:程序博客网 时间:2024/03/29 03:06
 

Question/Problem/Abstract:

 

Ever wanted to fire up some threads in your application, let them do some time consuming stuff and then report the results to the user? This caused some synchronisation trouble, didn't it? Shutting down your app while threads where still running, updating the user interface...

Here is a unit that will give a good bases to avoid all kinds of multi threading trouble.

 

Answer:

 

{ -----------------------------------------------------------------------

  Newer version and test bench can be found here:

  http://codecentral.borland.com/codecentral/ccweb.exe/listing?id=17700

  -----------------------------------------------------------------------

 

  Smart Thread Lib

  Version 1.01

  Copyright (c) 2002 by DelphiFactory Netherlands BV

 

  What is it:

  Provides an easy way to use threads.

 

  Usage:

  Create your threads as TSmartThreads and manage them

  using the SmartThreadManager global object.

 

  For more information about threads in delphi:

  http://www.pergolesi.demon.co.uk/prog/threads/ToC.html

 

  For example on how to use this unit for with a Indy blocking

  socket TCP/IP client:

   http://www.delphi3000.com/articles/article_3047.asp

}

 

unit SmartThreadLib;

 

{ Defining the DefaultMessageHandler causes the messages send

  by the threads to be displayed on screen if no OnMessage handler

  is assigned. This is only for debugging purposes (as GUI routines should

  not be located in this unit). }

{$DEFINE DefaultMessageHandler}

 

interface

 

uses

  SysUtils, Classes, Contnrs

{$IFDEF DefaultMessageHandler}

  ,QDialogs

{$ENDIF}

  ;

 

resourcestring

  SForcedStop = 'Thread ''%s'' forced to stop';

 

{ EThreadForcedShutdown exception will be raised inside a thread when

  it has to stop running. }

type

  EThreadForcedShutdown = class(Exception);

 

{ The ThreadMessageEvent is called by a smart thread but within the

  context of the main thread and provides the ability to easily show messages

  to the user. }

type

  TThreadMessageEvent = procedure(Sender : TObject; const AMessage : string) of object;

 

{ The SmartThread.

  Usage:

    1. Create a descendent class.

    2. Override the SmartExecute.

    3. Call Check from within SmartExecute on a regular base. This

       routine will raise an EThreadForcedShutdown exception if the thread

       has to stop. The exception is handled by this base class, you do

       not need to handle it.

 

  Additional tips:

    - You can use the Msg() procedure to show messages to the user without

      having to worry about synchronisation problems.

    - You can override GetMustStop() to add additional checks that could

      cause a thread to do a forced shutdown.

    - SmartExecute is started directly after calling Create()

    - The thread is FreeOnTerminate.

    - SmartThreads are based on the idea that threads are independant. You

      should not keep a pointer to the new thread, because you can never know

      if this pointer is still valid.

      Instead let your threads communicate using a global object. As an

      example se the SmartThreadManager.

}

type

  TSmartThread = class(TThread)

  private

    FMsg : string;

    procedure DoMessage;

  protected

    function GetMustStop: Boolean; virtual;

    procedure Msg(const Msg : string); virtual;

    procedure Check;

 

    procedure Execute; override;

    procedure SmartExecute; virtual;

  public

    constructor Create; virtual;

    property MustStop : Boolean read GetMustStop;

  end;

 

 

{ The SmartThreadManager: Global object that manages all TSmartThread's.

 

  The SmartThreads register themselfs at this manager before

  executing, and unregister just before destroying itself.

 

  - SmartThreads are based on the idea that threads are independant. You

  should not keep a pointer to the new thread, because you can never know

  if this pointer is still valid.  Instead let your threads communicate

  using a global object. The manager provides an event called OnMessage.

  The threads can trigger this event by calling their Msg() method. The

  OnMessage event runs in the context of the main thread. So screen updates

  can be performed. The Sender parameter is the thread which has send the

  message. This thread is guarantied to exist and is in suspended mode during

  the execution of the eventhandler.

  (If 'DefaultMessageHandler' is defined during compilation, the message will

  be displayed automaticly when no handler is assigned.)

 

  - Set ShutDown to True to shutdown all the smart threads.

 

  - ThreadCount returns the number of currently running smart threads

 

  - All threads are terminated automaticaly when the manager is destroyed.

    The manager is created and destroyed by the initialization and

    finalization section in this unit.

}

type

  TSmartThreadManager = class

  private

    FThreadListSync : TMultiReadExclusiveWriteSynchronizer;

    FShutDownSync : TMultiReadExclusiveWriteSynchronizer;

    FThreadList : TObjectList;

    FShutDown : Boolean;

    FOnMessage : TThreadMessageEvent;

    function GetShutDown: Boolean;

    procedure SetShutDown(const Value: Boolean);

    function GetThreadCount: Integer;

  protected

    procedure RegisterThread(AThread : TSmartThread);

    procedure UnregisterThread(AThread : TSmartThread);

    procedure DoMessage(Sender : TObject; AMessage : string);

  public

    constructor Create;

    destructor Destroy; override;

 

    procedure LimitThreadCount(Max : Integer);

 

    property ThreadCount : Integer read GetThreadCount;

    property Shutdown : Boolean read GetShutDown write SetShutDown;

    property OnMessage : TThreadMessageEvent read FOnMessage write FOnMessage;

  end;

 

var

  SmartThreadManager  : TSmartThreadManager;

 

implementation

 

 

{ TSmartThread }

 

procedure TSmartThread.Check;

begin

  // raise exception when the thread needs to stop

  if MustStop then

    raise EThreadForcedShutdown.CreateFmt(SForcedStop, [Self.ClassName]);

end;

 

constructor TSmartThread.Create;

begin

  // create in suspended mode

  inherited Create(True);

  // init

  FreeOnTerminate := True;

 

  // register at the manager

  SmartThreadManager.RegisterThread(Self);

 

  // run the thread

  Suspended := False;

end;

 

procedure TSmartThread.DoMessage;

{ Call this method using Synchronize(DoMessage)

  to make sure that we are running in the context of the main thread }

begin

  // Notify the manager about the message

  SmartThreadManager.DoMessage(Self, FMsg);

end;

 

procedure TSmartThread.Execute;

begin

  try

    try

      // Perform code to be implemented by descendant class

      SmartExecute;

    except

      // ignore forced shutdown exceptions

      On E : EThreadForcedShutdown do {nothing};

    end;

  finally

    // unregister at the manager

    SmartThreadManager.UnregisterThread(Self);

  end;

  // After unregistering the smart thread should shutdown

  // as fast as possible and do not perform any more tasks.

end;

 

function TSmartThread.GetMustStop: Boolean;

begin

  // We must stop if the thread is marked as terminated

  //   or if the manager wants to shutdown

  Result := Terminated or SmartThreadManager.Shutdown;

end;

 

procedure TSmartThread.Msg(const Msg: string);

begin

  // save message for later use by DoMessage

  FMsg := Msg;

  // call the DoMessage in the context of the main thread

  Synchronize(DoMessage);

end;

 

procedure TSmartThread.SmartExecute;

begin

  // do nothing, method can be implemented by descendant

end;

 

{ TSmartThreadManager }

 

constructor TSmartThreadManager.Create;

begin

  inherited Create;

  // init

  FShutdownSync := TMultiReadExclusiveWriteSynchronizer.Create;

  FThreadListSync := TMultiReadExclusiveWriteSynchronizer.Create;

  FThreadList := TObjectList.Create(False);

end;

 

destructor TSmartThreadManager.Destroy;

begin

  // manager is shutting down - cause al threads to stop

  SetShutDown(True);

 

  // wait for all threads to have stopped

  LimitThreadCount(0);

 

  // now we can cleanup

  FThreadList.Free;

  FThreadListSync.Free;

  FShutDownSync.Free;

 

  inherited Destroy;

end;

 

 

procedure TSmartThreadManager.DoMessage(Sender: TObject; AMessage: string);

const

  SMsg = '%s message: ''%s''';

begin

  // Call eventhandler

  if Assigned(FOnMessage) then

    FOnMessage(Sender, AMessage)

{$IFDEF DefaultMessageHandler}

  else // if there is no eventhandler, display the message on screen

    ShowMessage(Format(SMsg, [Sender.ClassName, AMessage]));

{$ENDIF}

end;

 

function TSmartThreadManager.GetShutDown: Boolean;

{ ThreadSafe

  Returns the Shutdown flag

}

begin

  FShutdownSync.BeginRead;

  try

    Result := FShutDown;

  finally

    FShutdownSync.EndRead;

  end;

end;

 

function TSmartThreadManager.GetThreadCount: Integer;

{ ThreadSafe

  Returns the number of running smart threads

}

begin

  FThreadListSync.BeginRead;

  try

    Result := FThreadList.Count;

  finally

    FThreadListSync.EndRead;

  end;

end;

 

procedure TSmartThreadManager.LimitThreadCount(Max: Integer);

{ Should only be called in the context of the main thread.

 

  Returns until the number of runnning smart threads is

  equal or lower then the Max parameter.

}

begin

  while GetThreadCount > Max do

    if not CheckSynchronize then

      Sleep(100);

end;

 

procedure TSmartThreadManager.RegisterThread(AThread: TSmartThread);

{ Thread safe

  Is called by the TSmartThread.Create constructor to register

  a new smart thread.

}

begin

  FThreadListSync.BeginWrite;

  try

    if FThreadList.IndexOf(AThread) = -1 then

      FThreadList.Add(AThread);

  finally

    FThreadListSync.EndWrite;

  end;

end;

 

procedure TSmartThreadManager.SetShutDown(const Value: Boolean);

{ Thread Safe

  Set the shutdown flag.

}

begin

  // make sure this is an different value

  if Value <> GetShutDown then

  begin

    FShutdownSync.BeginWrite;

    try

      // set new value

      FShutDown := Value;

    finally

      FShutdownSync.EndWrite;

    end;

  end;

end;

 

procedure TSmartThreadManager.UnregisterThread(AThread: TSmartThread);

{ Thread Safe

  Called by TSmartThread.Execute after the TSmartThread.SmartExecute

  has finished (or an exception was raised). it unregisters the thread.

}

begin

  FThreadListSync.BeginWrite;

  try

    FThreadList.Remove(AThread)

  finally

    FThreadListSync.EndWrite;

  end;

end;

 

initialization

  // fire up the manager

  SmartThreadManager := TSmartThreadManager.Create;

finalization

  // going down

  SmartThreadManager.Free;

end.

 

 

原创粉丝点击