17

現在 NLog によってまだ開かれている間に、プロセスがログ ファイルを読み込もうとしているというファイル共有の問題があります。この問題を診断したところ、驚くべきことがわかりました。以下は失敗します。

using (var fileStream1 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read))
using (var fileStream2 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.Read))
{
}

2 番目のFileStreamコンストラクター呼び出しは次のエラーで失敗します。

System.IO.IOException was unhandled
  Message=The process cannot access the file 'c:\...\test.file' because it is being used by another process.
  Source=mscorlib
  StackTrace:
       at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
       at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath)
       at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)

FileStreamこれは、最初のものは読書を共有する意欲を示しているという事実にもかかわらずです. さらに驚いたのは、これが機能することです。

using (var fileStream1 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read))
using (var fileStream2 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
}

ええと、はい、2 番目のストリームを開くときに追加のアクセスを要求すると、実際には問題が回避されます。なぜそうなのか、私は完全に困惑しており、何かを誤解しているとしか思えません。API ドキュメントを読みましたが、実際の動作とは逆に、これがどのように動作するかについて、現在のメンタル モデルをサポートしているだけです。

ドキュメントからの補足的な引用を次に示します。

この列挙型の一般的な用途は、2 つのプロセスが同じファイルから同時に読み取ることができるかどうかを定義することです。たとえば、ファイルが開かれ、読み取りが指定されている場合、他のユーザーはそのファイルを読み取り用に開くことはできますが、書き込み用には開くことができません。

ここに別の宝石があります:

次の FileStream コンストラクターは、既存のファイルを開き、他のユーザーに読み取り専用アクセス (読み取り) を許可します。

FileStream s2 = new FileStream(name, FileMode.Open, FileAccess.Read, FileShare.Read);

誰でもこの動作に光を当てることができますか? これを .NET 4 % Windows XP でテストしています。

4

4 に答える 4

18
 var fileStream2 = new FileStream(..., FileShare.Read)

これは多くのプログラマーをつまずかせます。これにより読み取り共有が追加されたと誰もが想定しています。そうではありませんでした。元のファイル アクセス リクエストでは既に読み取りが許可されており、再度指定しても何も変わりません。代わりに、書き込み共有を拒否します。誰かがすでに書き込みアクセス権を持っているため、それは機能しません。そしてそれを使用している場合、その権利を削除することはできません。そのため、ファイルへのアクセス要求は失敗します。

FileShare.Writeを含める必要があります。

于 2013-05-14T22:30:43.070 に答える
1

実際に起こることは、fileStream2によって書き込み (または追加) のために既に開かれているファイルへの後続のアクセスを変更できないということfileStream1です。

fileStream2FileShare.Readファイルへのアクセス権を持つプロセスがない場合にのみ、その後のアクセスのために を「レガシー」のままにして、ファイルを正常に開きますWrite。さらに、この例では、同じプロセスについて話しています。ファイル ストリームのプロパティを別のファイル ストリームから変更するのはあまり意味がありませんね。

おそらく、次の比較はそれをさらによく説明しています。

// works
using (var fileStream1 = new FileStream("test.file", FileMode.OpenOrCreate, FileAccess.Read, FileShare.ReadWrite))
using (var fileStream2 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read))
using (var fileStream3 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
}

// fails
using (var fileStream1 = new FileStream("test.file", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
using (var fileStream2 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read))
using (var fileStream3 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
}

私の意見では、 の説明フレーズは次のFileShare.Readとおりです。

その後、ファイルを読み取り用に開くことができます。

として読む必要があります

ファイルへの後続のアクセスは、既存のロックへのアクセスを含め、読み取りのみに制限されます。

[アップデート]

私はコードを解析していませんが、これらの 2 つのリンクは、コンストラクターの内部機能に光を当てる可能性があるようです。

内部 FileStream ctor

内部 FileStream Init メソッド

于 2013-05-14T14:05:44.317 に答える
0

渡す 4 番目のパラメーター

share
プロセスがファイルを共有する方法を決定する定数。

他のユーザーがファイルを開くことができるモードを決定します。したがって、ファイル共有モード「読み取り」でファイルを開こうとしていて、すでに同じファイルを書き込みモードで開いている場合、操作は失敗します。

于 2013-05-14T13:15:07.300 に答える