3

我有一个应用程序,可以在主 PC 上每秒将信息记录到每日文本文件中。网络上使用相同应用程序的从属 PC 想要将此文本文件复制到其本地驱动器。我可以看到会有文件访问问题。

这些文件每个不应大于 30-40MB。网络将是 100MB 以太网。我可以看到复制过程可能需要超过 1 秒的时间,这意味着记录 PC 需要在读取文件时打开文件进行写入。

文件写入(记录)和文件复制过程的最佳方法是什么?我知道有标准的 Windows CopyFile() 过程,但是这给了我文件访问问题。还有使用 fmShareDenyNone 标志的 TFileStream,但这也偶尔会给我带来访问问题(例如每周 1 次)。

这是完成这项任务的最佳方式是什么?

我当前的文件记录:

procedure FSWriteline(Filename,Header,s : String);
var LogFile : TFileStream;
line : String;
begin
     if not FileExists(filename) then
     begin
          LogFile := TFileStream.Create(FileName, fmCreate or fmShareDenyNone);
          try
             LogFile.Seek(0,soFromEnd);
             line := Header + #13#10;
             LogFile.Write(line[1],Length(line));
             line := s + #13#10;
             LogFile.Write(line[1],Length(line));
          finally
                 logfile.Free;
          end;
     end else begin
         line := s + #13#10;
         Logfile:=tfilestream.Create(Filename,fmOpenWrite or fmShareDenyNone);
         try
            logfile.Seek(0,soFromEnd);
            Logfile.Write(line[1], length(line));
         finally
            Logfile.free;
         end;
     end;
end;

我的文件复制过程:

procedure DoCopy(infile, Outfile : String);
begin
     ForceDirectories(ExtractFilePath(outfile)); //ensure folder exists
     if FileAge(inFile) = FileAge(OutFile) then Exit; //they are the same modified time
     try
        { Open existing destination }
        fo := TFileStream.Create(Outfile, fmOpenReadWrite or fmShareDenyNone);
        fo.Position := 0;
     except
           { otherwise Create destination }
           fo := TFileStream.Create(OutFile, fmCreate or fmShareDenyNone);
     end;
     try
        { open source }
        fi := TFileStream.Create(InFile, fmOpenRead or fmShareDenyNone);
        try
           cnt:= 0;
           fi.Position := cnt;
           max := fi.Size;
           {start copying }
           Repeat
                 dod := BLOCKSIZE; // Block size
                 if cnt+dod>max then dod := max-cnt;
                 if dod>0 then did := fo.CopyFrom(fi, dod);
                 cnt:=cnt+did;
                 Percent := Round(Cnt/Max*100);
           until (dod=0)
        finally
               fi.free;
        end;
     finally
            fo.free;
     end;
end;
4

3 回答 3

2

我建议不要一开始就关闭和重新打开共享文件。由于您每秒都对其进行写入,因此这只是不必要的开销。

在 Master 端,创建并关闭文件(该fmCreate标志不能与其他标志一起使用!),然后fmOpenWrite以共享模式重新打开它,保持fmShareDenyWrite打开状态,并在需要时写入。

在从端,fmOpenRead以共享模式打开文件,保持fmShareDenyNone打开状态,每秒读取一次。无需每次都通过网络复制整个共享文件。那是浪费带宽。只需读取过去几秒钟内写入的任何新数据即可。如果 Slave 需要将数据存储在本地文件中,那么它可以独立于共享文件管理一个单独的本地文件,在需要时将新数据推送到本地文件中。

于 2011-01-14T20:37:21.050 回答
1

要处理您的特定偶尔重复出现的问题:

你没有说你使用的是什么版本的Delphi。

TFileStream.Create() 构造函数中存在一个错误,直到并包括 2007 版(至少)。这可能解释了您偶尔出现的并发问题。

话虽如此,我认为该错误更有可能导致文件未按预期创建(当另外指定 ShareMode 时),这可能反过来导致您的并发问题。

解决这个问题的一种方法可能是当需要创建文件时,首先创建文件,然后简单地打开它以作为单独的构造函数调用进行写入 - 这实际上使文件创建成为一个单独的步骤,文件写入过程的一致部分:

  if not FileExists(filename) then
  begin
    // There may be a more efficient way of creating an empty file, but this 
    //  illustrates the approach

    LogFile := TFileStream.Create(FileName, fmCreate);
    LogFile.Free;

    line := Header + #13#10 + s + #13#10;
  end
  else
    line := s + #13#10;

  Logfile:=tfilestream.Create(Filename,fmOpenWrite or fmShareDenyNone);
  try
    logfile.Seek(0,soFromEnd);
    Logfile.Write(line[1], length(line));
  finally
    Logfile.free;
  end;
于 2011-01-14T05:46:53.207 回答
0

Use the standard Append file creation/opening command, use write to update the log, and close the file immediately.

Use a job on the operating system to copy/move the files; have it retry and to launch at a frequency greater than what you require.

If you want to do it from within Delphi then use MoveFile to move the whole thing.

You might want to wrap both the log writes and the moves in try-except so they can be retried a reasonable number of times if the file system (NTFS on Windows?) doesn't resolve the concurrency for you. In the worst case, either:

  1. The file got moved and it gets recreated and written to.
  2. The file is not moved right away because it is being written to.

If the OS doesn't resolve the race condition, then you will have to give priority to the starved action using a semaphore/lock.

于 2011-01-14T04:43:59.777 回答