Actia
asked on
Problem with WaitForSingleObject
Hello,
Please have a look at the code below.
What it should be doing is:
1. User clicks the SpawnButton
2. ('Calculation began...') info is displayed
3. Thread starts the execution- it calculates if the entered number is prime
4. Once it's finished it displays the result and SetEvent(hEvent)
5. Main Thread waits until Thread finishes the calculation. It uses WaitForSingleObject(hEvent , INFINITE).
the problem is that 'WaitForSingleObject(hEven t, INFINITE)' never returns the value. It looks like this function
just never finds out that SetEvent(hEvent) was ever called- and it should because it IS CALLED.
What do I do wrong?
Jack
unit PrimeForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TPrimeFrm = class(TForm)
SpawnButton: TButton;
NumEdit: TEdit;
ResultsMemo: TMemo;
procedure SpawnButtonClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
PrimeFrm: TPrimeFrm;
implementation
uses PrimeThread;
{$R *.dfm}
procedure TPrimeFrm.SpawnButtonClick (Sender: TObject);
var
NewThread: TPrimeThrd;
begin
ResultsMemo.Lines.Add('Cal culation began...');
NewThread := TPrimeThrd.Create(True);
NewThread.FreeOnTerminate := True;
try
NewThread.TestNumber := StrToInt(Trim(NumEdit.Text ));
ResetEvent(hEvent);
NewThread.Resume;
except
on EConvertError do
begin
NewThread.Free;
ShowMessage('That is not a valid number!');
end;
end;
case WaitForSingleObject(hEvent , INFINITE) of //***PROBLEM
WAIT_TIMEOUT : ResultsMemo.Lines.Add('It takes too long');
WAIT_OBJECT_0 : ResultsMemo.Lines.Add('OK' );
WAIT_FAILED : ResultsMemo.Lines.Add('Fai led');
end;
//***it NEVER returns the value, looks like SetEvent(hEvent) was never executed- but it was in UpdateResults!
end;
end.
xxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxx xxxxx
unit PrimeThread;
interface
uses
Classes, Windows;
type
TPrimeThrd = class(TThread)
private
FTestNumber: integer;
FResultString: string;
protected
function IsPrime: boolean;
procedure Execute; override;
procedure UpdateResults;
public
property TestNumber: integer write FTestNumber;
end;
var
hEvent : THandle;
implementation
uses SysUtils, Dialogs, PrimeForm;
function TPrimeThrd.IsPrime: boolean;
var iter: integer;
begin
result := true;
if FTestNumber < 0 then
begin
result := false;
exit;
end;
if FTestNumber <= 2 then
exit;
for iter := 2 to FTestNumber - 1 do
begin
if (FTestNumber mod iter) = 0 then
begin
result := false;
{exit;}
end;
end;
end;
procedure TPrimeThrd.Execute;
begin
if IsPrime then
FResultString := IntToStr(FTestNumber) + ' is prime.'
else
FResultString := IntToStr(FTestNumber) + ' is not prime.';
Synchronize(UpdateResults) ;
end;
procedure TPrimeThrd.UpdateResults;
begin
PrimeFrm.ResultsMemo.Lines .Add(FResu ltString);
SetEvent(hEvent);
end;
initialization
hEvent := CreateEvent(nil, True, false, PChar('MCS_WFSO_ExampleEve nt'));
finalization
CloseHandle(hEvent);
end.
Please have a look at the code below.
What it should be doing is:
1. User clicks the SpawnButton
2. ('Calculation began...') info is displayed
3. Thread starts the execution- it calculates if the entered number is prime
4. Once it's finished it displays the result and SetEvent(hEvent)
5. Main Thread waits until Thread finishes the calculation. It uses WaitForSingleObject(hEvent
the problem is that 'WaitForSingleObject(hEven
just never finds out that SetEvent(hEvent) was ever called- and it should because it IS CALLED.
What do I do wrong?
Jack
unit PrimeForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TPrimeFrm = class(TForm)
SpawnButton: TButton;
NumEdit: TEdit;
ResultsMemo: TMemo;
procedure SpawnButtonClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
PrimeFrm: TPrimeFrm;
implementation
uses PrimeThread;
{$R *.dfm}
procedure TPrimeFrm.SpawnButtonClick
var
NewThread: TPrimeThrd;
begin
ResultsMemo.Lines.Add('Cal
NewThread := TPrimeThrd.Create(True);
NewThread.FreeOnTerminate := True;
try
NewThread.TestNumber := StrToInt(Trim(NumEdit.Text
ResetEvent(hEvent);
NewThread.Resume;
except
on EConvertError do
begin
NewThread.Free;
ShowMessage('That is not a valid number!');
end;
end;
case WaitForSingleObject(hEvent
WAIT_TIMEOUT : ResultsMemo.Lines.Add('It takes too long');
WAIT_OBJECT_0 : ResultsMemo.Lines.Add('OK'
WAIT_FAILED : ResultsMemo.Lines.Add('Fai
end;
//***it NEVER returns the value, looks like SetEvent(hEvent) was never executed- but it was in UpdateResults!
end;
end.
xxxxxxxxxxxxxxxxxxxxxxxxxx
unit PrimeThread;
interface
uses
Classes, Windows;
type
TPrimeThrd = class(TThread)
private
FTestNumber: integer;
FResultString: string;
protected
function IsPrime: boolean;
procedure Execute; override;
procedure UpdateResults;
public
property TestNumber: integer write FTestNumber;
end;
var
hEvent : THandle;
implementation
uses SysUtils, Dialogs, PrimeForm;
function TPrimeThrd.IsPrime: boolean;
var iter: integer;
begin
result := true;
if FTestNumber < 0 then
begin
result := false;
exit;
end;
if FTestNumber <= 2 then
exit;
for iter := 2 to FTestNumber - 1 do
begin
if (FTestNumber mod iter) = 0 then
begin
result := false;
{exit;}
end;
end;
end;
procedure TPrimeThrd.Execute;
begin
if IsPrime then
FResultString := IntToStr(FTestNumber) + ' is prime.'
else
FResultString := IntToStr(FTestNumber) + ' is not prime.';
Synchronize(UpdateResults)
end;
procedure TPrimeThrd.UpdateResults;
begin
PrimeFrm.ResultsMemo.Lines
SetEvent(hEvent);
end;
initialization
hEvent := CreateEvent(nil, True, false, PChar('MCS_WFSO_ExampleEve
finalization
CloseHandle(hEvent);
end.
ASKER
>>This cannot work because the WaitForSingleObject blocks the main thread until the signal occurs.
Synchronize in the TPrimeThrd blocks because the main thread is blocked and cannot handle messages. Synchonization relies on messages.
Right...!
>>All in all you are completely off-track.
If you block the main thread then there is no use for a secondary thread becasue the main thread could do it instead.
I know- I wrote this software just to learn about Threads.
What I've done to make it work is:
procedure TPrimeThrd.Execute;
begin
if IsPrime then
FResultString := IntToStr(FTestNumber) + ' is prime.'
else
FResultString := IntToStr(FTestNumber) + ' is not prime.';
SetEvent(hEvent); //*** put this line here
Synchronize(UpdateResults) ; //instead of inside Synchronize
end;
and now it works fine, but maybe I can do it even better?
Jack
Synchronize in the TPrimeThrd blocks because the main thread is blocked and cannot handle messages. Synchonization relies on messages.
Right...!
>>All in all you are completely off-track.
If you block the main thread then there is no use for a secondary thread becasue the main thread could do it instead.
I know- I wrote this software just to learn about Threads.
What I've done to make it work is:
procedure TPrimeThrd.Execute;
begin
if IsPrime then
FResultString := IntToStr(FTestNumber) + ' is prime.'
else
FResultString := IntToStr(FTestNumber) + ' is not prime.';
SetEvent(hEvent); //*** put this line here
Synchronize(UpdateResults)
end;
and now it works fine, but maybe I can do it even better?
Jack
ASKER CERTIFIED SOLUTION
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
The basic idea to block the main thread is simply wrong.
Your change is not really correct either. Now the main thread outputs first (or at least can output first) so for a real world example the result from the thread is not yet available the instant the event is received.
Your change is not really correct either. Now the main thread outputs first (or at least can output first) so for a real world example the result from the thread is not yet available the instant the event is received.
ASKER
>The basic idea to block the main thread is simply wrong.
Your change is not really correct either. Now the main thread outputs first (or at least can output first) so for a real world example the result from the thread is not yet available the instant the event is received.
Right... maybe I will explain what I exactly want to achieve. My application is supposed to send messages to external electronic unit. It is 2 way communication like this one:
1. My application: send a command
2. My application WAIT for max 3 seconds to get a reply (in the code I provided I replaced electronic unit with prime number calculation)
3. After getting the reply -> send next command
3.1 Wrong or not reply -> raise an exception and do not send next command
So... are you saying that my approach is simple wrong? If so- which one is better?
Jack
Your change is not really correct either. Now the main thread outputs first (or at least can output first) so for a real world example the result from the thread is not yet available the instant the event is received.
Right... maybe I will explain what I exactly want to achieve. My application is supposed to send messages to external electronic unit. It is 2 way communication like this one:
1. My application: send a command
2. My application WAIT for max 3 seconds to get a reply (in the code I provided I replaced electronic unit with prime number calculation)
3. After getting the reply -> send next command
3.1 Wrong or not reply -> raise an exception and do not send next command
So... are you saying that my approach is simple wrong? If so- which one is better?
Jack
SOLUTION
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
Synchronize in the TPrimeThrd blocks because the main thread is blocked and cannot handle messages. Synchonization relies on messages.
All in all you are completely off-track.
If you block the main thread then there is no use for a secondary thread becasue the main thread could do it instead.
What you should do is create a Delphi event in TPrimeFrm (something like OnThreadUpdate) and the implementation is in TPrimeThrd.UpdateResults.