In my previous post, I promised to publish some examples of OTL usage. Here is the first one - a simplification of the Hello, World program. This example doesn't write anything to the screen, it just makes a noise.
Start by creating a new Delphi VCL application. Drop a button on the form and write OnClick handler.
procedure TfrmTestOTL.btnBeepClick(Sender: TObject);
begin
CreateTask(Beep, 'Beep').Run;
end;
This will create a threaded task with name 'Beep' and main method Beep. Then the code will create a new thread and start executing this task in the context of the newly created thread. That's all, folks!
To make the example fully functional, add OtlTask unit to the uses list and write the Beep method.
procedure TfrmTestOTL.Beep(task: IOmniTask);
begin
MessageBeep(MB_ICONEXCLAMATION);
end;
Run the program, click the button. It will beep!
If you don't believe that the code is executed in a secondary thread, place a breakpoint on the MessageBeep call and check the Thread Status window. It will clearly indicate that the breakpoint was triggered from a secondary thread.
This example is included in the OTL repository in folder tests/0_Beep.
"This project currently has no downloads."----from google code
ReplyDeleteTrue. At this moment, you can only do SVN checkout. Instructions are on the Source tab.
ReplyDeleteYou could use this function to make the thread's name show up in the thread list:
ReplyDeleteprocedure SetThreadName(const _Name: string);
var
ThreadNameInfo: TThreadNameInfo;
begin
ThreadNameInfo.FType := $1000;
ThreadNameInfo.FName := PChar(_Name);
ThreadNameInfo.FThreadID := $FFFFFFFF;
ThreadNameInfo.FFlags := 0;
try
RaiseException($406D1388, 0, SizeOf(ThreadNameInfo) div SizeOf(LongWord), @ThreadNameInfo);
except
// ignore
end;
end;
twm
That is not a bad idea.
ReplyDeleteI had problems with such code in older Delphis. I don't remember it well, but I think that the debugger stopped on this RaiseException when I was single-stepping through the code. I'll have to try it in D2007 ...
New version of the OtlTask unit with SetThreadName support was just uploaded to the Google Code.
ReplyDeleteAmazing threading approach.
ReplyDeleteI really love it.
Thanks, I'm glad you like it.
ReplyDeleteSpread the good word around :)
Hi,
ReplyDeleteI have tried to make it beeps more than once, so I have modified the procedure Beep to:
for I := 0 to 30 do
begin
MessageBeep(MB_ICONEXCLAMATION);
Sleep(1000);
end;
But then I have found that the demo is not responding until the beeping finished.
What have I done wrong?
Bo Chen Lin
That is because in this simple test, interface returned from the CreateTask is not stored anywhere.
ReplyDeleteprocedure TfrmTestSimple.btnBeepClick(Sender: TObject);
begin
CreateTask(Beep, 'Beep').Run;
end;
Therefore, this interface is destroyed when btnBeepClick exits. When the task control's destructor is called, it waits for the task to complete and that's causing the blockage.
You should store result of the CreateTask in a form field, as the next demos do.
Taking your suggestion, I added FThread to hold the interface:
ReplyDeleteprocedure TfrmTestSimple.btnBeepClick(Sender: TObject);
begin
FThread := CreateTask(Beep, 'Beep').Run;
end;
Not it is responding with the beeps.
But there is a problem again, it won't close until the beep finished. So I thought the thread should be terminated when the form was closing:
procedure TfrmTestSimple.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FThread.Terminate(1000);
end;
then the demo can be closed without having to wait all the beeps finished. This is all good until I added FastMM4 in the project file. It
reports some memory leaks. I thought it might due to that FThread is not cleared. So I managed to clear the interface reference by:
destructor TfrmTestSimple.Destroy;
begin
FThread := nil;
inherited;
end;
This reduced the memory leaks, but there are still two leaks of TOmniTask and TOmniThread.
What I don't understand is that if I let the 30 beeps finished, then there is no memory leak.
Why?
This is all as expected. The demo won't close because the background thread is waiting in the for/sleep. Terminate will kill it but that will lead to lost memory.
ReplyDeletePlease study other OTL examples to see how the background thread can be properly implemented.