14

ファイルを作成し、処理のために開いて、終了したら削除するマルチスレッドの C# アプリケーションがあります。このアプリケーションでは、1 ~ 100 個のファイルが処理されると予想されます。処理後にファイルを削除しようとすると、多少ランダムに (アプリケーションのマルチスレッドの性質が原因である可能性が最も高い) 共有違反が発生します。私の直感によると、ヴィック、ファイルを削除しようとする前に、ファイルを適切に閉じたり破棄したりしていませんでした。そして、それがすべてのファイルで発生した場合、私は私の腸に行きますが、そうではありません. ということで、どこが間違っているのかを探っていきます。このタイプの例外をデバッグする方法についての指針を持っている人はいますか? それが理にかなっている場合は、ファイルのスタック トレースを確認したいと思います。

疑似コードを表示しようとしますが、私の質問は、このタイプの例外をデバッグする方法についてです。

アプリケーション イベント:

Operation Start += 新しいプロセッサを作成します。

Transfer File += Processor.ProcessFile および新しい Document オブジェクトを Processor の Document コレクションに追加します (ファイルではなくパスとして)

操作完了+= Processor.Aggregate ファイル、ファイルの内容を含む新しいファイルを作成します。このメソッドが終了すると、ProcessorFinished が呼び出されます。

プロセッサ イベント:

プロセッサの終了+= Application_CleanUpProcessor。このメソッドでは、プロセッサを破棄します。プロセッサはドキュメント オブジェクトを破棄し、ファイルを削除します。

4

1 に答える 1

32

コードのデバッグにより多くの時間を費やす必要があるかどうかを確認する簡単な方法が必要であることを受け入れるか、コードが問題ないことを証明するための適切なテストを作成する必要がある場合は、他のプロセスがファイルを使用していないことを証明する簡単な方法が必要です。 . したがって、次のように仮定します。

  • ファイルが (ネットワーク共有ではなく) ローカル ディスク上にあり、かつ
  • ウイルス対策、Windows のインデックス作成、またはその他の何かがコード以外のファイルをロックしていると思われます
  • したがって、テストを作成する前に、イン/アウトをすばやく修飾する方法が必要です

プログラムを実行できるようにして、共有違反が発生するまでにそのファイルに何が起こったのかを確認したいと考えています。

私はこれを行います:

1. sysinternals から ProcMon をダウンロードします (10 秒)

Procmon は優れたツールです。すべてのプロセスで起こっていることを順番にフィルターして確認できます。Microsoft の sysinternals の procmon へのリンク

2. Procmon を抽出して実行し、フィルターとハイライトを追加します (30 秒)

procmon を開き、"Path" "begins with" のフィルタを追加します。"

procmon へのフィルターの追加

「Result」「is」「SHARING VIOLATION」のハイライトを追加します。

共有違反フィルターを追加する

最後に、例外が発生するまでプログラムを実行してから、パス列で共有違反のあるファイルを右クリックし、['< filename here >' を含める] を選択して、他のすべての結果を削除します。例外の原因となったファイルのすべてのアクティビティを確認できます...

誰がそのファイルをロックしたかを示すProcmon

procmon に慣れたい場合は、これをすべて偽装するために使用したコードを次に示します。ファイルをロックするサイドスレッドと、ファイルをロックしようとするメインスレッドがあります。C# コンソール アプリを作成するだけですぐに使用できます。次のようになります。

これは私が以前に作成したものです-ロックとロックの犯人

したがって、2 分もかからずに、自分のコードに問題があるのか​​、それとも他の何かに問題があるのか​​を確認できます。先日これを使用して、Com コンポーネントが実際に代替ファイル ストリームを使用していることを確認したため、ネットワーク ドライブを使用しようとすると例外がスローされました。そこでは、単体テストをいくらしても役に立ちませんでした。

共有違反を強制するテスト コードは次のとおりです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.IO;
using System.Threading;

internal class Program
{
    private static int lockPoint = 0;

    private static void Main(string[] args)
    {
        const string testFile = @"H:\test\test.txt";

        FileInfo testFileInfo = new FileInfo(testFile);

        if (!testFileInfo.Directory.Exists)
        {
            testFileInfo.Directory.Create();
        }

        //  Clear our example
        if (testFileInfo.Exists)
        {
            testFileInfo.Delete();
        }

        //  Create the test file
        using (FileStream fs = File.Create(testFile))
        using (StreamWriter sw = new StreamWriter(fs))
        {
            sw.WriteLine("test file content");
        }

        Task iLockTheFileFirst = new Task(() => {
            using (FileStream fsThread = File.Open(testFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            {
                Console.WriteLine("iLockTheFileFirst: I opened the file");

                //  Set lockPoint to 1 and let main try and open the file
                Interlocked.Exchange(ref lockPoint, 1);

                //  Wait until the main thread sets lockPoint to 3
                const int ifEqualTo3 = 3;
                const int replaceWith4 = 4;
                while (Interlocked.CompareExchange(ref lockPoint, replaceWith4, ifEqualTo3) != ifEqualTo3)
                {
                    Console.WriteLine("iLockTheFileFirst: Waiting for main thread to let me finish");
                    Thread.Sleep(1000);
                }
            }
            Console.WriteLine("iLockTheFileFirst: I have closed the file");
        });
        //  Start the thread and lock the file
        iLockTheFileFirst.Start();

        //  Now spin until the lockPoint becomes 1
        const int ifEqualTo1 = 1;
        const int replaceWith2 = 2;
        //  If lockPoint is equal to 1 (i.e. the main thread wants us to finish), then move it to 2
        while (Interlocked.CompareExchange(ref lockPoint, replaceWith2, ifEqualTo1) != ifEqualTo1)
        {
            Console.WriteLine("Main thread: waiting for iLockTheFileFirst to open the file");
            Thread.Sleep(1000);
        }

        try
        {
            Console.WriteLine("Main thread: now I'll try opening the file");
            using (FileStream fsMain = File.Open(testFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
            {
                Console.WriteLine("Main thread: I opened the file, which shouldn't be possible");
            }
        }
        catch (IOException ioex)
        {
            Console.WriteLine("Main thread: IOException: " + ioex.Message);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Main thread: some other exception: " + ex.Message);
        }

        //  Set lockPoint to 3 and let other thread finish
        Interlocked.Exchange(ref lockPoint, 3);

        //  Wait for other thread to finish
        const int ifEqualTo4 = 4;
        const int replaceWith5 = 5;
        while (Interlocked.CompareExchange(ref lockPoint, replaceWith5, ifEqualTo4) != ifEqualTo4)
        {
            Thread.Sleep(10);
        }

        Console.WriteLine("Main thread: Press enter to finish");
        Console.ReadLine();
    }
}

それはすべての人々です!

于 2013-03-18T19:21:22.447 に答える