Multithreaded file write via an existing object - multithreading

Application Description:
I have an application that allows a user to run multiple concurrent queries via threads (up to 100 at once).
I have a class that I use for logging errors. If an error occurs in the application, I create an instance of the class and call a procedure to write the error to a log file.
Question:
I need to make the error logging code thread safe. I've noticed that if a lot of threads are running at the same time and generating the same error (e.g. cannot connect to database), I'm getting i/o error 32 (caused by the application attempting to write to a file that's already open).
As a quick and dirty fix, I've put the code that writes to file in a try... except block inside a repeat loop. If there is an exception (e.g. the file has already been opened by another instance of the class, kicked off by another thread), then it sets a flag to "false". The loop continues to execute until the flag is "true" (i.e. no error writing to file), as follows:
procedure TErrorLogging.logError(error: string);
var
f: textfile;
ok: boolean;
begin
repeat
ok := true;
try
assignfile(f, fLogFilename);
if fileExists(fLogFilename) then append(f) else rewrite(f);
writeln(f, error);
closefile(f);
except
ok := false;
end;
until ok;
end;
I'm aware that the correct way to protect blocks of code is by using Critical Sections, but I'm not sure how I'd implement that, given that there are a number of different threads that use the logging class, and each instance of the thread has its own instance of the logging class that it uses to write to file (so they're not all just synchronizing against the same block of code).
The options, as I can see them:
Use the code as above. Are there any issues with leaving this code as it is? It's a quick and dirty fix, but it works.
Use a global TCriticalSection (how?).
Use a single procedure somewhere that creates an instance of the logging class, which the threads will synchronize against (which defeats the object of having a logging class, I suppose).

Creating instance of a logging class whenever you want to append log entry is wrong as well as opening and closing a log file over and over again. I would personally use one instance of a class which internally uses a string list and whose basic methods are thread safe. Something like this:
type
TErrorLog = class
private
FList: TStringList;
FLock: TRTLCriticalSection;
public
constructor Create;
destructor Destroy; override;
procedure Clear;
procedure Add(const ErrorText: string);
procedure SaveToFile(const FileName: string);
end;
implementation
{ TErrorLog }
constructor TErrorLog.Create;
begin
inherited Create;
InitializeCriticalSection(FLock);
FList := TStringList.Create;
end;
destructor TErrorLog.Destroy;
begin
EnterCriticalSection(FLock);
try
FList.Free;
inherited Destroy;
finally
LeaveCriticalSection(FLock);
DeleteCriticalSection(FLock);
end;
end;
procedure TErrorLog.Clear;
begin
EnterCriticalSection(FLock);
try
FList.Clear;
finally
LeaveCriticalSection(FLock);
end;
end;
procedure TErrorLog.Add(const ErrorText: string);
begin
EnterCriticalSection(FLock);
try
FList.Add(ErrorText);
finally
LeaveCriticalSection(FLock);
end;
end;
procedure TErrorLog.SaveToFile(const FileName: string);
begin
EnterCriticalSection(FLock);
try
FList.SaveToFile(FileName);
finally
LeaveCriticalSection(FLock);
end;
end;

Not knowing Delphi, as a general design rule (if possible), I would have your logError function insert into a thread safe Array, ArrayList, Queue object or such that you have available, and then have it write to the file in the background, perhaps every 5-10 seconds or so. This should not only take care of the i/o problem, but should also scale to thousands of writes per second in case you want to log other events for debugging or such.

Related

Creating MainForm on a TThread

I have a Delphi 2010 application that exports a DLL and has the library header. It creates its MainForm in a TThread, like so:
var
ActiveThread: TActive;
type
TActive= class(TThread)
protected
procedure Execute; override;
end;
procedure TActive.Execute;
begin
Application.Initialize;
Application.CreateForm(MyForm, form);
Application.Run;
end;
begin
ActiveThread := TActive.Create(true);
ActiveThread.FreeOnTerminate := true;
ActiveThread.Resume;
end.
Whenever I load this DLL through the LoadLibrary function, the application runs fine. (Apparently it uses the thread that I passed to LoadLibrary as the main thread and has no issues)
But if I attempt to export this DLL to an actual EXE, by changing the generated output in Options -> Application. and changing the header from library to program and then build it and execute the output EXE instead of loading the DLL through the windows api, the application hangs when attempting to create the form, specifically at Application.CreateForm(MyForm, form);. If I remove the Application initialization from the thread and place it on the main routine, it runs just fine.
The form I'm trying to render is just an empty form. Any ideas?
When compiling this code as a program, at runtime it will try to terminate itself when end. is reached, before the worker thread even has a chance to run, which could possibly (and likely) happen after the Application object has been destroyed. You would have to wait for the worker thread to finish its work before letting the program exit, eg:
program MyProgram;
uses
Classes, Forms, MyForm;
type
TActive = class(TThread)
protected
procedure Execute; override;
end;
procedure TActive.Execute;
begin
Application.Initialize;
Application.CreateForm(TMyForm, MyForm);
Application.Run;
end;
var
ActiveThread: TActive;
begin
ActiveThread := TActive.Create(False);
ActiveThread.WaitFor;
ActiveThread.Free;
end.
But, there is really no good reason to ever use a worker thread like this, this defeats the whole purpose of using a thread, so you may as well just get rid of it altogether:
program MyProgram;
uses
Forms, MyForm;
begin
Application.Initialize;
Application.CreateForm(TMyForm, MyForm);
Application.Run;
end.
On the other hand, if you are trying to share common code between program and library projects, then you can wrap the Application code inside of a function and let the project decide which thread calls the function, eg:
unit MyApp;
interface
procedure RunMyApp;
implementation
uses
Forms, MyForm;
procedure RunMyApp;
begin
Application.Initialize;
Application.CreateForm(TMyForm, MyForm);
Application.Run;
end;
end.
program MyProgram;
uses
MyApp;
begin
RunMyApp;
end.
library MyLibrary
uses
Classes, MyApp;
type
TActive = class(TThread)
protected
procedure Execute; override;
end;
procedure TActive.Execute;
begin
RunMyApp;
end;
var
ActiveThread: TActive;
begin
ActiveThread := TActive.Create(True);
ActiveThread.FreeOnTerminate := True;
ActiveThread.Resume;
end.

Simple multithreading Delphi

I am still quite new to threading. I want to create a procedure which tests for a valid internet connection while the main thread creates the necessary forms. The code snippet stops at the end of the constructor with a 'Cannot call Start on a running or suspended thread' error. And for some reason the main form is closed after this error
constructor TPingThread.Create(IDThread: Integer);
begin
Self.FID:=IDThread;
Self.FreeOnTerminate:=true;
end;
destructor TPingThread.Destroy;
begin
EndThread(FID);
inherited;
end;
procedure TPingThread.Execute;
var
iTimeOuts, K: Byte;
sWebpage: String;
begin
inherited;
iTimeOuts:=0;
FIdPing:=TIdHTTP.Create(nil);
for k:=1 to 3 do
begin
Try
FIdPing.ConnectTimeout:=2000;
sWebpage:=FIdPing.Get('http://www.google.co.za')
Except
On Exception do inc(iTimeOuts);
End;
end;
if iTimeOuts=3 then MessageDlg('A working internetconnection is needed to reset your password',mtWarning,[mbOK],0);
if iTimeOuts=0 then FInternetConnection:=false
else FInternetConnection:=true;
FreeAndNil(FIdPing);
end;
There are some problems with your code:
You need to call the inherited constructor:
constructor TPingThread.Create(IDThread: Integer);
begin
inherited Create(false); // Or true to create a suspended thread
Self.FID:=IDThread;
...
Remove the inherited call in the Execute method, since this is an abstract declaration of TThread. Not an error per se, but should be avoided for clarity.
Use a try/finally after creating FIdPing in the Execute method.
As #mjn says, there is no need to call EndThread(), since the TThread handles this for you.
Calling the VCL MessageDlg() from a thread is not thread safe. You need to synchronize the call or use the Application.MessageBox, a Delphi wrapper to windows MessageBox. Best solution would be to skip the dialog and pass an error message to the main thread, which would need to know this error anyway.

Threaded initialization and cleanup in delphi

I was reading the answer to another question here about the need to run coinitialize and couninitialize to connect to ADO objects. I need to do something similar to perform soap calls in a thread.
Is there a way to override a TThread object to have something automatically run before and after execute inside the thread?
For this example, we're converting to a SOAP backend and have to do a ton of that and it would save a bit of time to just override a new SOAP friendly TThread than to add coinitialize and couninitialze to every thread. But in general, initializing and cleaning up the thread inside the thread sometimes seems like a good idea. Right now it seems like you can only do one or the other.
Perhaps you want something like this:
type
TMyThread = class sealed(TThread)
private
FOnBeforeExecute: TProc;
FOnExecute: TProc;
FOnAfterExecute: TProc;
protected
procedure Execute; override;
public
property OnBeforeExecute: TProc read FOnBeforeExecute write FOnBeforeExecute;
property OnExecute: TProc read FOnExecute write FOnExecute;
property OnAfterExecute: TProc read FOnAfterExecute write FOnAfterExecute;
end;
procedure TMyThread.Execute;
begin
if Assigned(OnBeforeExecute) then
OnBeforeExecute;
try
if Assigned(OnExecute) then
OnExecute;
finally
if Assigned(OnAfterExecute) then
OnAfterExecute;
end;
end;
I made it a sealed class so that you cannot replace Execute with something that breaks the design. The added benefit is that you can decouple the thread procedure from the implementing class using events.
If you want to take care on specific initialization and finalization doing it with events like in David's answer means that you have to assign those events for every thread you create. And that means either adding a specific constructor to pass them in or creating the threads in suspended mode.
Personally I don't really like having to remember to do all those things and would therefore go for a more polymorphic solution:
type
TInitializeFinalizeThread = class(TThread)
protected
procedure InitializeExecution; virtual;
procedure FinalizeExecution; virtual;
procedure InternalExecute; virtual;
procedure Execute; override;
end;
procedure TInitializeFinalizeThread.Execute;
begin
InitializeExecution;
try
InternalExecute;
finally
FinalizeExecution;
end;
end;
Threads needing to do Ole Stuff could then have a common base that takes care of the initialization and finialization:
type
TOleThread = class(TInitializeFinalizeThread)
protected
procedure InitializeExecution; override;
procedure FinalizeExecution; override;
end;
procedure TOleThread.InitializeExecution;
begin
CoInitialize;
end;
procedure TOleThread.FinalizeExecution;
begin
CoUninitialize;
end;
This means that classes that are actually going to do something can just inherit from TOleThread and be assured that the initialization and finalization have been taken care of, so they only need to override InternalExecute.
type
TWordMailMergeThread = class(TInitializeFinalizeThread)
protected
procedure InternalExecute; override;
end;
procedure TWordMailMergeThread.InternalExecute;
begin
// Whatever you need this to do.
end;
Though they are of course free to override the InitializeExecution and FinalizeExecution methods to set up and quit the connection to the OleServer (Word in this example) instead of doing it in the InternalExecute.

AsyncCall with Delphi 2007

What I basically want is to start AsyncCall and proceed with my code loading. I have Interface section that consumes lots of time (600+ms) and I want to load this code in independent thread.
I've tried to use AsyncCall to make something like this:
procedure Load;
begin
...
end;
initialization
AsyncCall(#Load, []); // or LocalAsyncCall(#Load)
However, this Load procedure actually starts in Main thread and not in the new created thread. How can I force the Load procedure to be loaded in any thread other than MainThread?
I can create TThread and Execute this but I want to force AsyncCall or LocalAsyncCall or anything from AsyncCall library to make to work.
Thanks for your help.
Have you tried something like this?:
procedure Load;
begin
if GetCurrentThreadId <> MainThreadID then
Beep;
end;
var a: IAsyncCall;
initialization
a := AsyncCall(#Load, []);
a.ForceDifferentThread;
ForceDifferentThread() tells AsyncCalls that the assigned function must
not be executed in the current thread.
The problem is that your code is not retaining the IAsyncCall interface that is returned by the AsyncCall function.
AsyncCall(#Load, []);
//AsyncCall returns an IAsyncCall interface,
//but this code does not take a reference to it
Because of this, the interface that is returned has its reference count decremented to zero as soon as the initialization section completes. This therefore frees the object that implements the interface which does this:
destructor TAsyncCall.Destroy;
begin
if FCall <> nil then
begin
try
--> FCall.Sync; // throw raised exceptions here
finally
FCall.Free;
end;
end;
inherited Destroy;
end;
The key line is the call to Sync which forces the asynchronous call to be executed to completion. All this happens in the main thread which explains the behaviour that you report.
The solution is that you simply need to keep the IAsyncCall interface alive by storing it in a variable.
var
a: IAsyncCall;
initialization
a := AsyncCall(#Load, []);
In the real code you need to ensure that Load had completed before running any code that is reliant on Load. When your program reached a point where it required Load to have been called it has to call Sync on the IAsyncCall interface.
So you might write it something like this.
unit MyUnit;
interface
procedure EnsureLoaded;
implementation
uses
AsyncCalls;
....
procedure Load;
begin
....
end;
var
LoadAsyncCall: IAsyncCall;
procedure EnsureLoaded;
begin
LoadAsyncCall := nil;//this will effect a call to Sync
end;
initialization
LoadAsyncCall := AsyncCall(#Load, []);
end.
The call EnsureLoaded from other units that required Load to have run. Or, alternatively, call EnsureLoaded from any methods exported by MyUnit that depended on Load having run. The latter option has much better encapsulation.

Exist any drawback if i use a callback function inside of a thread to comunicate with the main thread of my app?

i wrote a Thread.descendent class, and to comunicate my thread with the main thread i use a callback function so i am wondering if is a valid solution or instead i must use windows messages?
type
TMyCallBack= procedure(const Param1,Param2: string) of object;
TMyThread= class(TThread)
private
P1 : string;
P2 : string;
MyCallBack : TMyCallBack;
procedure Process;
public
Constructor Create(CallBack : TMyCallBack); overload;
destructor Destroy; override;
procedure Execute; override;
end;
procedure TMyThread.Process;
begin
FCallBack(P1,P2);
end;
constructor TMyThread.Create(CallBack : TMyCallBack);
begin
inherited Create(False);
FreeOnTerminate := True;
MyCallBack := CallBack;
end;
procedure TMyThread.Execute;
begin
while True and not Terminated do
begin
AResult:= FListener.GetResult(Param1,Param2,5000);
if not VarIsNull(AResult) then
begin
P1:=AResult.Value1;
P2:=AResult.Value2;
Synchronize(Process);
end;
end;
end;
As long as you use Synchronize you should be fine.
If you run the callback via Synchronize, it's OK, as most Delphi implementations:
create a callback structure, containing the callback and an event handle
append the callback structure to a locked global list
post a message to the main thread, to wake it from WaitMessage or alike
wait on the event until the callback completes
This may or may not be better than using raw window messages, as:
the callback list is checked in clearly defined places and as such its not as much eligible for reentrancy issues
for the same reasons, its certainly a bit less performant
it may cause problems with modal windows and native popup menus, which allow sent message processing, but may bypass the synchronization list handling in some cases
As long as the callback processing must mot be waitable/cancellable, and you can tell for sure it doesn't do anything that might cause sent message processing (as most windows-message-related routines do!), you may prefer using SendMessage, with appropriate parameter marshaling.

Resources