web-dev-qa-db-ja.com

C#で、2つのプロセスが同じファイルを読み書きしている場合、プロセスロックの例外を回避するための最良の方法は何ですか?

次のファイルでコードを読み取ります。

using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
    using (TextReader tr = new StreamReader(fileStream))
    {
        string fileContents = tr.ReadToEnd();
    }
}

そして、次のファイルはコードを書きます:

using (TextWriter tw = new StreamWriter(fileName))
{
    tw.Write(fileContents);
    tw.Close();
}

次の例外の詳細が表示されます。

別のプロセスで使用されているため、プロセスはファイル 'c:\ temp\myfile.txt'にアクセスできません。

これを回避する最良の方法は何ですか?読者は例外の受信時に再試行する必要がありますか、それとももっと良い方法がありますか?

リーダープロセスはFileSystemWatcherを使用して、ファイルがいつ変更されたかを認識していることに注意してください。

また、この例では、2つのプロセス間で文字列を共有する別の方法を探しているnotです。

25
Iain

書き込み用にファイルを開き、書き込みアクセスのみをロックすることにより、他のユーザーがファイルを引き続き読み取ることができます。

例えば、

using (FileStream stream = new FileStream(@"C:\Myfile.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.Read))
{
   // Do your writing here.
}

他のファイルアクセスでは、ファイルを読み取り用に開くだけで書き込みは行わず、読み取りと書き込みの共有を許可します。

using (FileStream stream = new FileStream(@"C:\Myfile.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
   // Does reading  here.
}

リーダーが常に最新のファイルを読み取るようにする場合は、誰かがファイルに書き込んでいることを示すロックファイルを使用する必要があります(ただし、慎重に実装しないと競合状態になる可能性があります)。例外を読み取りおよび処理するために開くときは、書き込み共有をブロックしてください。そうすることで、排他的アクセスを取得するまで再試行できます。

36
Jeff Yates

名前付きミューテックスを作成する場合、書き込みアプリケーションでミューテックスを定義し、ミューテックスが解放されるまで読み取りアプリケーションを待機させることができます。

したがって、現在FileSystemWatcherで動作している通知プロセスで、ミューテックスを待機する必要があるかどうかを確認します。待機している場合は、待機してから処理します。

これが MutexのVBの例 で、私が見つけたものです。C#に変換するのは簡単です。

6
Mitchel Sellers

FileShare.Noneでファイルを開く特別な理由はありますか?これにより、ファイルが他のプロセスによって開かれるのを防ぎます。

FileShare.WriteまたはFileShare.ReadWriteは、他のプロセス(アクセス許可に従う)がファイルの読み取り中にファイルを開いて書き込むことを許可する必要がありますが、読み取り中にファイルが変更されるのを監視する必要があります。開封時の内容が参考になります。

ただし、これらの答えはすべて等しく有効です。最善の解決策は、ファイルに対して何をしようとしているのかによって異なります。ファイルが変更されないことを保証しながら読み取ることが重要な場合は、ロックして、後続の例外を処理します。あなたのコードを書く;同時に読み書きすることが重要な場合は、FileShare定数を変更してください。

3
symonc

これにはMutexオブジェクトを使用できます。

2
leppie

ファイルが書き込まれている場合、ファイルのステータスを確認するプロセスを取得します。これは、ロックファイルの存在によって実行できます(つまり、空になっている可能性があるこの他のファイルの存在により、メインファイルへの書き込みが妨げられます)。

ただし、これはフェイルセーフではありません。2つのプロセスが同時にロックファイルを作成する可能性があるためです。ただし、書き込みをコミットする前にこれを確認できます。

プロセスでロックファイルが検出された場合は、単純にスリープ/待機して、事前に定義された間隔で再試行してください。

2
Anthony

リーダーとライターの両方に再試行メカニズムが必要です。また、FileShareは、リーダーの場合はFileShare.readに、ライターの場合はFileShare.noneに設定する必要があります。これにより、書き込みの進行中にリーダーがファイルを読み取らないようにする必要があります。

リーダー(再試行を除く)は

using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
    using (TextReader tr = new StreamReader(fileStream))
    {
        string fileContents = tr.ReadToEnd();
    }
}

ライター(再試行を除く)は次のようになります。

FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
using (TextWriter tw = new StreamWriter(fileStream))
{
    tw.Write(fileContents);
    tw.Close();
}
2
Iain

最善の方法は、ファイル転送/所有権転送メカニズムの上にアプリケーションプロトコルを配置することです。 「ロックファイル」メカニズムは、古くから存在している古いUNIXハックです。最善の方法は、ファイルをリーダーに「渡す」ことです。これを行う方法はたくさんあります。ランダムなファイル名でファイルを作成し、その名前をリーダーに「与える」ことができます。これにより、ライターは非同期で別のファイルに書き込むことができます。 「ウェブページ」の仕組みを考えてみてください。 Webページには、画像、スクリプト、外部コンテンツなどの詳細情報への「リンク」があります。サーバーは、必要な「リソース」の一貫したビューであるため、そのページを渡します。次に、ブラウザーはページの説明(HTMLファイルまたはその他の返されたコンテンツ)に基づいて適切なコンテンツを取得し、必要なものを転送します。

これは、使用する最も復元力のあるタイプの「共有」メカニズムです。ファイルを書き込み、名前を共有し、次のファイルに移動します。 「名前の共有」の部分は、両方の当事者(読者と作家)がコンテンツが「完全」であることに同意することを確実にするアトミックな受け渡しです。

1
grwww

一時ファイルへの書き込み、書き込みの終了時に、ファイルの名前の変更/リーダーが探している場所や名前へのファイルの移動。

1
KristoferA