Threading in Delphi for .NET - Timer Classes
(Page 8 of 15 )
The System.Threading.Timer Class
The .NET Framework has three different Timer classes that are used for different purposes. System.Threading.Timer uses threads from the thread pool and instead of firing an event, it uses the specified callback method whose definition is shown below:
TimerCallback = procedure (state: System.Object) of object;
There are four overloaded constructors of the Timer class. All of the constructors are identical except for the type of the last two parameters. The callback parameter is the method to call when the timer fires. Use the state parameter to pass additional information in a System.Object parameter to the callback method. dueTime specifies how long to wait before the callback method is called. This value is specified in milliseconds. Finally, period, also specified in milliseconds, indicates how long to wait between successive calls to the callback method. Use one of the Change() methods to change dueTime or period after creating the Timer instance. Taken from timerthread.dpr, Listing 14.5 demonstrates how to use the Timer class.
Listing 14.5 Declaration of the System.Threading.Timer Class Example
1: program timerthread;
2: {$APPTYPE CONSOLE}
3: uses
4: System.Threading;
5:
6: type
7: TD4DNTimerClass = class
8: public
9: // Alarm will be called when the Timer fires
10: procedure Alarm(state : System.Object);
11: end;
12:
13: procedure TD4DNTimerClass.Alarm(state : System.Object);
14: begin
15: writeln(AppDomain.GetCurrentThreadID,' Bzzzz. Time to wake up!');
16: if assigned(state) then
17: writeln('You passed me: ', state.ToString);
18: end;
19:
20: var
21: tc : TD4DNTimerClass;
22: t : Timer;
23: begin
24: // create an instance of our class
25: tc := TD4DNTimerClass.Create;
26: // create the timer to call Alarm only once, after delaying 1 second (1000 ms)
27: t := Timer.Create(tc.Alarm, System.Object('Some additional info'), 1000 , 0);
28: writeln(AppDomain.GetCurrentThreadID,' Waiting for the timer to fire.');
29: // give the timer a chance to fire
30: Thread.Sleep(2000);
31: writeln(AppDomain.GetCurrentThreadID,' Done!');
32: end.
Note: Find the code on the CD: \Code\Chapter 14\Ex04\.
Delegates
A delegate is a type-safe callback mechanism inherited from System.Delegate. Delegates require a method that is called at the appropriate time. Delegates that descend from System.MulticastDelegate are capable of handling multiple methods. Although delegates are normally called in an synchronous manner using the Invoke() method, the BeginInvoke() method allows for calling delegate methods asynchronously. Only delegates that have one method to call are capable of using the BeginInvoke() method. Unfortunately, the Delphi 8 compiler does not recognize a method pointer as a delegate class instance. Fortunately, BeginInvoke() can still be called by using Reflection. Listing 14.6 demonstrates how to execute a delegate asynchronously.
Listing 14.6 Executing Delegates Asynchronously
1: program AsyncDelegate;
2: {$APPTYPE CONSOLE}
3: uses
4: System.Reflection,
5: System.Threading;
6:
7: type
8: TMyDelegate = procedure of object;
9:
10: TMyClass = class
11: private
12: FOnDoSomething : TMyDelegate;
13: public
14: procedure CallDelegate;
15: property OnDoSomething : TMyDelegate read FOnDoSomething write FOnDoSomething;
16: end;
17:
18: TAnotherClass = class
19: public
20: procedure DoFoo;
21: end;
22:
23: procedure TMyClass.CallDelegate;
24: var
25: obj : System.Object;
26: t : System.Type;
27: m : MethodInfo;
28: parms : array [0..1] of System.Object;
29: begin
30: writeln('CallDelegate');
31: if Assigned(FOnDoSomething) then
32: begin
33: // writeln('call delegate');
34: // Normally this would look similar to
35: // @FOnDoSomething.BeginInvoke(nil, nil);
36: // but the compiler doesn't support BeginInvoke, since
37: // it thinks it's just a pointer to a method.
38: //
39: // the work around uses reflection to invoke the method
40: // first cast the "method pointer" to an object
41: obj := System.Object(@FOnDoSomething);
42: // now get the type of the FOnDoSomething
43: t := obj.GetType;
44: // now we gat search for a method named BeginInvoke
45: m := t.GetMethod('BeginInvoke');
46: // build the parameter list
47: parms[0] := nil;
48: parms[1] := nil;
49: // now we can call BeginInvoke with the parms
50: m.Invoke(obj, parms);
51: end;
52: end;
53:
54: procedure TAnotherClass.DoFoo;
55: begin
56: writeln(AppDomain.GetCurrentThreadID,' DoFoo');
67: end;
58:
59: var
60: c : TMyClass;
61: ac : TAnotherClass;
62:
63: begin
64: c := TMyClass.Create;
65: ac := TAnotherClass.Create;
66: writeln(AppDomain.GetCurrentThreadID,' assigning the delegate');
67: c.OnDoSomething := @ac.DoFoo;
68: writeln(AppDomain.GetCurrentThreadID,' calling the delegate');
69: c.CallDelegate;
70: // give the delegate some time to call it...
71: Thread.Sleep(2000);
72: writeln(AppDomain.GetCurrentThreadID,' Done');
73: end.
Note: Find the code on the CD: \Code\Chapter 14\Ex05\.
This chapter is from Delphi for .NET Developer's Guide, by Xavier Pacheco (Sams, 2004, ISBN: 0-672-32443-1). Check it out at your favorite bookstore today.
Buy this book now. |
Next: Writing Thread-safe Code .NET Style >>
More .NET Articles
More By Xavier Pacheco