1. ホーム
  2. c#

[解決済み】IOException: ファイル 'ファイルパス' は他のプロセスによって使用されているため、プロセスはアクセスできません。

2022-04-08 06:28:27

質問

あるコードがあり、それが実行されると IOException と言っています。

このプロセスはファイル 'filename' を使用中のため、アクセスできません。 別のプロセス

これはどういうことですか、またどうすればいいですか?

解決方法は?

原因は何ですか?

エラーメッセージは非常に明確です。ファイルにアクセスしようとしているのに、他のプロセス(または同じプロセス)がそのファイルで何かを行っている(そして、共有を許可していない)ために、アクセスできないのです。

デバッギング

特定のシナリオによっては、かなり簡単に解決できる(あるいはかなり理解しにくい)かもしれません。いくつか見てみましょう。

そのファイルにアクセスできるのはあなたのプロセスだけです。

を確認しましたか? その他 プロセスは、あなた自身のプロセスです。もし、そのファイルをプログラムの別の部分で開いていることが分かっているのなら、まず、使用後にファイルハンドルをきちんと閉じているかどうかをチェックする必要があります。このバグがあるコードの例を示します。

var stream = new FileStream(path, FileAccess.Read);
var reader = new StreamReader(stream);
// Read data from this file, when I'm done I don't need it any more
File.Delete(path); // IOException: file is in use

幸いなことに FileStream が実装されています。 IDisposable の中ですべてのコードをラップするのは簡単です。 using ステートメントを使用します。

using (var stream = File.Open("myfile.txt", FileMode.Open)) {
    // Use stream
}

// Here stream is not accessible and it has been closed (also if
// an exception is thrown and stack unrolled

このパターンは、例外が発生した場合にファイルが開かれたままにならないようにすることもできます(ファイルが使われている理由は、何か問題が発生し、誰もそれを閉じなかったということかもしれません。 この記事 の例)。

もし、すべてがうまくいっていて(例外が発生した場合でも、開いたファイルは必ず閉じている)、複数のスレッドが動作している場合、2つの選択肢があります:ファイルアクセスをシリアライズするためにコードを作り直すか(常に可能で、常に望んでいるわけではありません)。 リトライパターン . これはI/O操作ではかなり一般的なパターンです。何かをしようとして、エラーの場合は待って再試行します(例えば、Windowsシェルがファイルが使用中で、削除できないことを通知するのに時間がかかる理由を自問したことがありますか)。C#でこれを実装するのはとても簡単です。 ディスクI/O , ネットワーキング そして データベースアクセス ).

private const int NumberOfRetries = 3;
private const int DelayOnRetry = 1000;

for (int i=1; i <= NumberOfRetries; ++i) {
    try {
        // Do stuff with file
        break; // When done we can break loop
    }
    catch (IOException e) when (i <= NumberOfRetries) {
        // You may check error code to filter some exceptions, not every error
        // can be recovered.
        Thread.Sleep(DelayOnRetry);
    }
}

StackOverflowで非常によく見かける一般的なエラーにご注意ください。

var stream = File.Open(path, FileOpen.Read);
var content = File.ReadAllText(path);

この場合 ReadAllText() はファイルが使用中であるため失敗します ( File.Open() の前の行にある)。あらかじめファイルを開いておくことは、不要であるばかりでなく、間違いでもあるのです。同じことが、すべての File を返さない関数は ハンドル を作業中のファイルに追加します。 File.ReadAllText() , File.WriteAllText() , File.ReadAllLines() , File.WriteAllLines() などがあります(例えば File.AppendAllXyz() 関数)は、すべて単独でファイルを開いたり閉じたりします。

そのファイルにアクセスできるのはあなたのプロセスだけではありません。

あなたのプロセスがそのファイルにアクセスする唯一のものでない場合、相互作用はより困難になる可能性があります。A リトライパターン が役に立ちます(もし、そのファイルが他の誰にも開かれていないはずなのに開かれている場合は、Process Explorerのようなユーティリティーで を実行している。 ).

回避する方法

該当する場合は、必ず を使って ステートメントを使用してファイルを開くことができます。前の段落で述べたように、これは多くの一般的なエラーを回避するのに積極的に役立ちます ( この記事 に関する例として 使い方 ).

可能であれば、特定のファイルへのアクセスの所有者を決め、いくつかのよく知られたメソッドを通じてアクセスを集中化するようにしてください。例えば、プログラムが読み書きするデータファイルがある場合、すべてのI/Oコードを1つのクラスで管理する必要があります。そうすれば、デバッグが容易になるし(そこにブレークポイントを置けば、誰が何をしているかがわかるから)、(必要なら)複数のアクセスに対する同期ポイントにもなる。

I/O操作は常に失敗する可能性があることを忘れないでください。よくある例がこれです。

if (File.Exists(path))
    File.Delete(path);

もし 誰か の後にファイルを削除します。 File.Exists() よりも前に File.Delete() を投げることになります。 IOException を、誤って安全だと感じるかもしれない場所に設置する。

可能な限り リトライパターン を使用する場合、また FileSystemWatcher は、アクションを延期することを検討してください(通知はされますが、アプリケーションはまだそのファイルのみで作業している可能性があるため)。

高度なシナリオ

いつもそう簡単にはいかないので、誰かとアクセスを共有する必要があるかもしれません。例えば、最初から読んでいて、最後まで書いている場合、少なくとも2つの選択肢があります。

1) 同じものを共有する FileStream を、適切な同期関数を使って(なぜなら スレッドセーフでない ). 参照 これ これ の投稿を例として挙げます。

2)使用 FileShare を列挙し、他のプロセス(または自プロセスの他の部分)が同じファイルに同時にアクセスできるようにOSに指示します。

using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.Read))
{
}

この例では、書き込み用にファイルを開き、読み込み用に共有する方法を示しました。読み込みと書き込みが重なった場合、未定義または無効なデータになってしまうので、注意してください。これは、読み込みの際に処理しなければならない状況です。また、これでは stream スレッドセーフなので、何らかの方法でアクセスを同期させない限り、このオブジェクトを複数のスレッドで共有することはできません(以前のリンクを参照してください)。他の共有オプションも利用可能で、より複雑なシナリオを開くことができます。以下を参照してください。 MSDN をご覧ください。

一般的に N 制御されたシナリオでは、同時書き込みを有効にすることもできますが、これはこの回答内の数行のテキストで一般化することはできません。

次のようなことは可能ですか? アンロック 他のプロセスで使用されているファイルですか?安全とは限りませんし、そう簡単ではありませんが、そうです。 可能です .