Effect of Hyperthreading on a Thread Object for Windows Animation: Part 3
Update Thread Class
Paul Milenkovic
Copyright 2003
An animation update thread has state – the value of the thread handle, the signal event handle, and a window handle to post animation update messages – and it has methods that operate on or employ that state. It is natural to bundle state and methods into an object class.
The class UpdateThread has a method UpdateLoop to process the update thread loop along with a method UpdateCmd to respond to animation update messages posted by the update thread to a window handle. Thus the same class has code for both GUI and update threads, but a given method is invoked by only one thread. A Windows thread object cannot directly invoke an object method, but it can invoke a standalone function that receives a pointer to an UpdateThread instance as an argument, which it can use to forward thread processing to the UpdateLoop method. The UpdateThread class also needs to be linked to a cooperating window handle object. The method Set_upd_wH() creates a Windows thread object and initiates the update thread when supplied with a window handle; it terminates the update thread and destroys the thread object when it is supplied a zero-valued window handle; supplying a different window handle keeps the same thread but changes the window handle where animation update messages are posted.
The window handle will be owned by whatever object in the framework you are using to manage a window handle, but the underlying window class to that framework object needs to be extended to process the WM_USER+1000 message (saved in upd_msg). The necessary processing is explained in a comment to the source listing. Each class framework – MFC, Borland VCL – has its own set of classes for window handle controls that embed and manage the window handle, and class UpdateThread is linked to such a window class through the window handle so it is usable with all of these frameworks.
Virtual abstract method UpdateNext is invoked once for each animation update – one can override it in a class derived from UpdateThread, or one can rewrite it as an implemented method if UpdateThread is extended by rewriting it. A Delphi Pascal source listing to unit UpdThrd.pas follows
unit UpdThrd;
{Copyright 2003 Paul Milenkovic, 118 Shiloh Dr., Madison, WI 53705 U.S.A.
Permission granted to use, copy, excerpt or share the source code provided
this notice is maintained.}
interface
uses Windows, Messages;
type
UpdateThread = class
private
upd_wH: HWnd;
upd_msg: Integer;
running, fcheck_paint, fupdating: Boolean;
nsleep: Integer;
// Thread handle, event handle
upd_threadH, upd_nextH: THandle;
function UpdateLoop: Integer;
protected
procedure Set_updating(updating: Boolean);
// Override this method with update code
procedure UpdateNext; virtual; abstract;
public
property check_paint: Boolean read fcheck_paint write fcheck_paint;
property updating: Boolean read fupdating write Set_updating;
procedure UpdateCmd;
procedure Set_upd_wH(wH: HWnd; msg: Integer);
constructor Create;
destructor Destroy; override;
end;
implementation
function RunUpdThread(update: UpdateThread): Integer;
begin
Result := update.UpdateLoop;
end;
{UpdateThread}
function UpdateThread.UpdateLoop: Integer;
begin
Result := 0;
while running do begin
WaitForSingleObject(upd_nextH,1000);
if running then begin
Sleep(nsleep);
PostMessage(upd_wH,upd_msg,0,Integer(Self));
// WindowProc of upd_wH must implement:
// upd_msg: UpdateThread(LParam).UpdCmd;
end;
end;
EndThread(0);
end;
procedure UpdateThread.Set_updating(updating: Boolean);
begin
if running and updating and (not fupdating) then begin
fupdating := True;
SetEvent(upd_nextH);
end;
fupdating := updating;
end;
procedure UpdateThread.UpdateCmd;
var msg: TMsg;
begin
if updating then begin
if check_paint and PeekMessage(msg,0,WM_PAINT,WM_PAINT,PM_NOREMOVE) then
//If paint messages are backed up, signal update thread without doing
// next update so Sleep(nsleep) can dispatch paint messages from the
// active message loop -- main program, message box, or menu.
Inc(nsleep)
else begin
if nsleep > 0 then
Dec(nsleep);
UpdateNext;
end;
SetEvent(upd_nextH);
end;
end;
procedure UpdateThread.Set_upd_wH(wH: HWnd; msg: Integer);
var pid: DWORD;
begin
upd_msg := msg;
if (wH <> 0) and (upd_wH = 0) then begin
upd_wH := wH;
running := True;
upd_nextH := CreateEvent(nil,False,False,nil);
upd_threadH := BeginThread(nil,0,@RunUpdThread,Pointer(Self),0,pid);
SetThreadPriority(upd_threadH,THREAD_PRIORITY_LOWEST);
if updating then
SetEvent(upd_nextH);
end else
if (wH = 0) and (upd_wH <> 0) then begin
running := False;
SetEvent(upd_nextH);
WaitForSingleObject(upd_threadH,10000);
CloseHandle(upd_threadH);
CloseHandle(upd_nextH);
upd_wH := 0;
end else
upd_wH := wH;
end;
constructor UpdateThread.Create;
begin
inherited Create;
upd_wH := 0;
running := False;
fcheck_paint := True;
fupdating := False;
nsleep := 0;
end;
destructor UpdateThread.Destroy;
begin
Set_upd_wH(0,0);
inherited Destroy;
end;
end.