Reputation: 23
I am writing a Delphi dll which calculates and writes the result to a CSV file. The calling program is multithreaded, so one problem is the simultaneous multiple writing to the file which causes the crash of the calling program. I tried to use the critical section to lock the file writing, but crashes still occur. If I configure the program to use only one thread, the problem disappears. Below is my code:
library Question;
uses
SysUtils,
Classes,
Math,
SyncObjs;
{$R *.res}
Var
Outputfile: textfile;
CriticalSection: TCriticalSection;
CalNumb: integer = 0;
PrintString: String;
Threadvar
Cal1, Cal2, Cal1Last: double;
Function Calculator (input1, input2, input3, input4: double;
Factor: double; LastCal: Boolean; Print: integer): double stdcall;
Const
Divisor = 4;
Var
Temp: double;
Begin
Cal1Last:= Cal1;
Cal1:= (input1+ input2+input3+ input4)/Divisor;
Cal2:= (Cal1+Factor*Cal1Last)/2;
Temp:= Cal2 - Cal1Last;
If LastCal and (Print = 1) then
begin
CriticalSection:= TCriticalSection.Create;
Try
Try
Inc(CalNumb);
Assign(Outputfile, 'C:\Calculator\Result.csv');
If FileExists('C:\Calculator\Result.csv') then
Append(Outputfile) else rewrite (Outputfile);
If CalNumb = 1 then
begin
PrintString:= 'CalNumb' + ',' + 'Cal1' + ',' +
'Cal1Last' + ',' + 'Cal2' + ',';
Writeln(Outputfile, PrintString);
end;
Writeln(Outputfile,
CalNumb, ',', Cal1:5:2, ',', Cal1Last:5:2, ',', Cal2:5:2, ',');
Finally
Close(Outputfile);
End;
Finally
CriticalSection.Free;
End;
end;
If Cal1 <> 0 then Calculator:= Temp/Cal1 else Calculator:= 0;
End;
Exports
Calculator;
begin
end.
Are there any errors in my code? Multithread calculation and external file writing is essential for this project. Do you have any suggestion and comments? I am a beginner, so if you could post your code here, it will be a big help for me. Thank you very much in advance! //////////////////////////////////////////////////////////////////////////////////////////////////
Graymatter, thanks for your suggestion. After I make the changes you suggested, the application crashes before writing into the file. The previous version of dll can write certain lines of data into the file before the crash. In case I might make wrong changes, I post the changed part below. The other part of the code is not changed.
If LastCal and (Print = 1) then
begin
CriticalSection.Acquire;
Try
Try
Inc(CalNumb);
Assign(Outputfile, 'C:\Calculator\Result.csv');
If FileExists('C:\Calculator\Result.csv') then
Append(Outputfile) else rewrite (Outputfile);
If CalNumb = 1 then
begin
PrintString:= 'CalNumb' + ',' + 'Cal1' + ',' +
'Cal1Last' + ',' + 'Cal2' + ',';
Writeln(Outputfile, PrintString);
end;
Writeln(Outputfile,
CalNumb, ',', Cal1:5:2, ',', Cal1Last:5:2, ',', Cal2:5:2, ',');
Finally
Close(Outputfile);
End;
Finally
CriticalSection.Release;
End;
end;
If Cal1 <> 0 then Calculator:= Temp/Cal1 else Calculator:= 0;
End;
Exports
Calculator;
begin
end.
initialization
CriticalSection := TCriticalSection.Create;
finalization
CriticalSection.Free;
end;
Upvotes: 0
Views: 1367
Reputation: 6587
Creating a critical section object doesn't do any locking. You need to call Acquire
to get the lock and Release
to release the lock.
See Using Critical Sections in the online help.
The problem is that the critical section needs to be created when the DLL is loaded so you will need to do something like this:
begin
...
CriticalSection.Acquire;
try
...
// The code that needs to be locked goes inside here.
// In your case it would be the code that opens the file
// and appends the data.
...
finally
CriticalSection.Release;
end;
...
end;
You will have to move your code into a separate unit so you can include initialization
and finalization
blocks. These blocks will get executed when the DLL is first loaded and when it's unloaded respectively.
Your new unit would look like this:
unit UnitForDLL;
interface
function Calculator(input1, input2, input3, input4: double;
Factor: double; LastCal: Boolean; Print: integer): double; stdcall;
implementation
uses
SyncObjs, SysUtils;
var
...
CriticalSection: TCriticalSection;
...
function Calculator(input1, input2, input3, input4: double;
Factor: double; LastCal: Boolean; Print: integer): double; stdcall;
begin
...
CriticalSection.Acquire;
try
...
// The code that needs to be locked goes inside here.
// In your case it would be the code that opens the file
// and appends the data.
...
finally
CriticalSection.Release;
end;
...
end;
initialization
CriticalSection := TCriticalSection.Create;
finalization
CriticalSection.Free;
end.
Once you have done that your project itself will be changed to a much simpler:
library Question;
uses
SysUtils,
Classes,
UnitForDLL;
{$R *.res}
exports
Calculator;
begin
end.
Upvotes: 6