質問

マスター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;
役に立ちましたか?

解決

共有ファイルを何度も何度も閉じたり再開したりしないことをお勧めします。あなたは毎秒それに書くので、それはただ不必要なオーバーヘッドです。

マスター側で、ファイルを作成して閉じます( fmCreate フラグは他のフラグでは使用できません!) fmOpenWrite でモード fmShareDenyWrite 共有し、開いたままにして、必要に応じて書き込みます。

スレーブ側に、ファイルを開きます fmOpenRead でモード fmShareDenyNone 共有し、開いたままにして、毎秒読んでください。共有ファイル全体を毎回ネットワーク上にコピーする必要はありません。それは無駄な帯域幅です。過去数秒で書かれた新しいデータを読んでください。それだけです。スレーブがローカルファイルにデータを保存する必要がある場合、共有ファイルから独立して個別のローカルファイルを管理し、必要に応じて新しいデータをローカルファイルに押し込みます。

他のヒント

あなたの特定の時折の繰り返しの問題に対処するため:

使用しているデルファイのバージョンは言うことはありません。

tfilestream.create()コンストラクターにバージョン2007までのバグがあります(少なくとも)。これは、あなたの時折の並行性の問題を説明するかもしれません。

そうは言っても、バグは、予想どおりにファイルが作成されない可能性が高いと思います(シェルモードがさらに指定されている場合)、これは順番に並行性の問題につながる可能性があります。

これを回避する1つの方法は、ファイルを作成する必要がある場合に、最初にファイルを作成してから、別のコンストラクター呼び出しとして書き込むためにそれを開くだけです。

  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;

標準を使用します 追加 ファイルの作成/開くコマンド、使用 write ログを更新するには close すぐにファイル。

オペレーティングシステムのジョブを使用して、ファイルをコピー/移動します。それを再試行し、あなたが必要とするものよりも大きな周波数で起動します。

Delphi内からやりたい場合は、使用してください 移動ファイル 全体を動かすために。

あなたはログの書き込みと動きの両方を包みたいかもしれません try-except したがって、ファイルシステム(Windows上のNTFS?)があなたの同時性を解決しない場合、彼らは妥当な回数を再試行することができます。最悪の場合、どちらも次のとおりです。

  1. ファイルは移動し、再作成され、書き込みます。
  2. ファイルは、書かれているため、すぐには移動されません。

OSが人種条件を解決しない場合、セマフォ/ロックを使用して飢えたアクションを優先する必要があります。

「isfileinuse」などの関数を検索するか、次のようなものを使用できると確信しています。

// master
while IsFileInUse(*AFileName*) do
  Sleep(10);
write-content-to-file

// slave
while IsFileInUse(*AFileName*) do
  Sleep(10);
copy-file-to-a-special-location

そしてプレスト!!あなたは終わった!

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top