需要帮助Delphi多线程文件编写

BF2*_*015 0 delphi io multithreading writing file

我正在写一个Delphi dll,它计算并将结果写入CSV文件.调用程序是多线程的,因此一个问题是同时多次写入文件会导致调用程序崩溃.我试图使用临界区来锁定文件写入,但仍然会发生崩溃.如果我将程序配置为仅使用一个线程,则问题将消失.以下是我的代码:

    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.
Run Code Online (Sandbox Code Playgroud)

我的代码中有错误吗?多线程计算和外部文件编写对于此项目至关重要.你有什么建议和意见吗?我是初学者,所以如果你能在这里发布你的代码,对我来说将是一个很大的帮助.非常感谢你提前!////////////////////////////////////////////////// ////////////////////////////////////////////////

Graymatter,谢谢你的建议.在我建议您进行更改后,应用程序会在写入文件之前崩溃.之前版本的dll可以在崩溃之前将某些数据行写入文件.如果我可能做出错误的更改,我会在下面发布更改的部分.代码的另一部分没有改变.

          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;
Run Code Online (Sandbox Code Playgroud)

Gra*_*ter 6

创建临界区对象不会执行任何锁定.您需要调用Acquire以获取锁定并Release释放锁定.

请参阅联机帮助中的使用关键部分.

问题是在加载DLL时需要创建关键部分,因此您需要执行以下操作:

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;
Run Code Online (Sandbox Code Playgroud)

您必须将代码移动到一个单独的单元中,以便包含initializationfinalization阻止.首次加载DLL时以及分别卸载DLL时,将执行这些块.

你的新单位看起来像这样:

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.
Run Code Online (Sandbox Code Playgroud)

完成后,您的项目本身将更改为更简单:

library Question;

uses
  SysUtils,
  Classes,
  UnitForDLL;

{$R *.res}

exports
       Calculator;

begin
end.
Run Code Online (Sandbox Code Playgroud)