I am writing an application that needs to wait until a file exists in a directory. I have tried multiple approaches to this, and the only solution that works is using Sleep/Application.ProcessMessages.
Here's what I have tried:
Using Sleep/Application.ProcessMessages:
Result := False;
for i := 0 to iTimeout do
begin
if FileExists(fileName) do
begin
updateStatus('Conversion Completed');
Result := True;
Break;
end;
updateStatus(Format('Checking for file: %d Seconds', [i]));
Application.ProcessMessages;
Sleep(1000);
end;
This method works, except that I can't close the application while it's waiting. Also there are well documented issues with using Sleep/Application.ProcessMessages that I would rather avoid.
Using TThread/TEvent:
type
TMyThread = class(TThread)
private
FEventDone: TEvent;
public
constructor Create(CreateSuspended: boolean);
destructor Destroy;
procedure Execute; override;
property EventDone: TEvent read FEventDone;
end;
TformThreading = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
waitThread: TMyThread;
{ Private declarations }
public
{ Public declarations }
end;
var
formThreading: TformThreading;
implementation
{$R *.dfm}
{ TformThreading }
procedure TformThreading.Button1Click(Sender: TObject);
var
res: TWaitResult;
begin
try
waitThread.Start;
res := waitThread.EventDone.WaitFor(INFINITE);
case res of
wrSignaled: ShowMessage('wrSignaled');
wrTimeout: ShowMessage('wrTimeout');
wrAbandoned: ShowMessage('wrAbandoned');
wrError: ShowMessage('wrError');
wrIOCompletion: ShowMessage('wrIOCompletion');
end;
except
on E: Exception do
begin
ShowMessage(E.Message);
end;
end;
end;
procedure TformThreading.FormCreate(Sender: TObject);
begin
waitThread := TMyThread.Create(true);
end;
procedure TformThreading.FormDestroy(Sender: TObject);
begin
waitThread.Free;
end;
{ TMyThread }
constructor TMyThread.Create(CreateSuspended: boolean);
begin
inherited Create(CreateSuspended);
FEventDone := TEvent.Create;
end;
destructor TMyThread.Destroy;
begin
FEventDone.Free;
end;
procedure TMyThread.Execute;
begin
for i := 0 to iTimeout do
begin
if FileExists(fileName) do
begin
FEventDone.SetEvent;
Break;
end;
Application.ProcessMessages;
Sleep(1000);
end;
end;
I can't seem to get this to not freeze my main thread while it is waiting, but this seems like the correct approach if I can work out the freezing issue.
What is my best approach to solve my problem?
I can't seem to get this to not freeze my main thread while it is waiting.
When you wait on an object, that thread blocks until the object is signaled. So when your main threas waits for the event it is blocked. The behaviour that you observe is exactly to be expected.
Looking at your thread's code, it loops until a condition is met, signals an event, and then terminates. In other words, the event serves no purpose. You could remove it and instead wait for the thread.
Now, if you did that your code would do this:
So, as it stands, your thread achieves nothing. You may as well execute its code in the main thread. You are back where you started.
You need a different mindset. GUI programs are asynchronous in their UI. You need to follow that pattern. Don't wait in the main thread. Instead, use a separate thread by all means, but have that thread signal to the main thread when it is done.
The very simplest way for you to do that here is to implement an OnTerminate event handler for the thread. That will fire when the thread completes its work. The event handler will execute in the main thread.
More generally you could use Synchronize or Queue to signal events to the main thread, but in your case handling the termination event meets your needs.
Now to the body of the thread. Your approach is based on polling, with a sleep. That will work but it's not terribly pretty. The ReadDirectoryChangesW function is the system provided mechanism for you to receive notifications of changes in the file system. That would be a more elegant approach.
My advice is to fix the blocking problems first using your current polling approach. Once that is clear re-factor to use ReadDirectoryChangesW.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With