Подтвердить что ты не робот

Как отлаживать "Нарушение совместного доступа" при попытке удалить файл

У меня есть многопоточное приложение С#, которое создает файлы, открывает их для обработки, а затем удаляет их после завершения. Это приложение может ожидать от 1 до 100 файлов для обработки. В некоторой степени случайным образом (скорее всего, связано с многопоточным характером приложения), я получаю нарушение доступа, когда пытаюсь удалить файл после обработки. Моя кишка говорит, ну, Вик, ты не закрыл/не удалил файл, прежде чем пытаться его удалить. И я пошел бы с моей кишкой, если бы это произошло для каждого файла, но это не так. Итак, я пытаюсь выяснить, где я делаю ошибку. У кого-нибудь есть какие-либо указания относительно того, как отлаживать этот тип исключения? Мне бы очень хотелось увидеть трассировку стека в файле, если это имеет смысл.

Я попытаюсь показать псевдокод, однако мой вопрос связан с тем, как отлаживать этот тип исключения:

События приложения:

Начало работы + = Создать новый процессор.

Файл переноса + = Processor.ProcessFile и Добавить новый объект Document в коллекцию документов процессора (как путь, а не файл)

Операция завершена + = Processor.Aggregate files, создайте новый файл, содержащий содержимое файлов. Когда этот метод будет завершен, он вызовет ProcessorFinished.

События процессора:

Обработанный процессор + = Application_CleanUpProcessor. В этом методе я распоряжаюсь процессором, который, в свою очередь, удаляет объект документа, который удаляет файл.

4b9b3361

Ответ 1

Признавая, что вам нужен быстрый способ проверить, тратить ли больше времени на отладку кода или, может быть, написать хорошие тесты, чтобы доказать, что ваш код в порядке, то, что вы хотите, - это быстрый способ доказать, что никакой другой процесс не является используя ваш файл. Итак, давайте предположим:

  • Ваш файл находится на локальном диске (а не на сетевом ресурсе) и
  • вы подозреваете, что антивирус, индексирование Windows или что-то еще блокирует файл, отличный от вашего кода.
  • поэтому вам нужно быстро определить, что в/из перед написанием тестов

Вы хотите иметь возможность запускать свою программу и посмотреть, что произошло с этим файлом, до того, как вы нарушили нарушение.

Я бы сделал это:

1. Загрузить ProcMon из sysinternals (10 секунд)

Procmon - отличный инструмент, вы можете отфильтровать до того, что хотите видеть во всех процессах в последовательном порядке. Ссылка на procmon в sysinternals, от Microsoft

2. Извлеките и запустите Procmon, добавьте фильтры и блики (30 секунд)

Откройте procmon, добавьте фильтр для "Путь" "начинается с" ""

Adding a filter to procmon

Теперь добавьте выделение для "Result" "is" "SHARING VIOLATION"

Add a Sharing Violation filter

И, наконец, запустите свою программу, пока не получите исключение, затем щелкните правой кнопкой мыши файл с нарушением совместного доступа в столбце пути и выберите "Включить" < имя файла здесь > ", чтобы удалить все другие результаты. Теперь вы можете увидеть всю активность для файла, вызвавшего ваше исключение...

Procmon showing who locked that file

Если вы хотите получить удовольствие от procmon, вот код, который я использовал, чтобы подделать это для вас. Он имеет боковой поток, который блокирует файл и основной поток, который затем пытается заблокировать файл. Просто создайте приложение консоли С# и отпустите. Это выглядит так:

Here's one I made earlier - the lock and lock again culprit

Итак, менее чем за 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();
    }
}

Что все люди!