Стратегия доступа к файлам в многопоточной среде (веб-приложение) - PullRequest
6 голосов
/ 03 сентября 2008

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

Я сейчас сталкиваюсь с проблемами, потому что, очевидно, в многопоточной среде он падает, когда пытается получить доступ к данным, когда он все еще читает / записывает файл.

Это сбивает меня с толку, потому что я добавил объект для блокировки, и он всегда блокируется во время чтения / записи. Насколько я понимаю, при попытке доступа из других потоков будет сказано "подождать", пока не будет снята блокировка?

Просто, чтобы вы знали, я действительно новичок в многопоточной разработке, поэтому я полностью готов признать, что это бред с моей стороны:)

  • Я что-то упустил?
  • Какова лучшая стратегия доступа к файлам в многопоточной среде?

Редактировать

Извините - я должен был сказать, что используется ASP.NET 2.0 :)

Ответы [ 5 ]

6 голосов
/ 03 сентября 2008

Вот код, который я использую, чтобы убедиться, что файл не заблокирован другим процессом. Он не на 100% надежен, но большую часть времени выполняет свою работу:

    /// <summary>
    /// Blocks until the file is not locked any more.
    /// </summary>
    /// <param name="fullPath"></param>
    bool WaitForFile(string fullPath)
    {
        int numTries = 0;
        while (true)
        {
            ++numTries;
            try
            {
                // Attempt to open the file exclusively.
                using (FileStream fs = new FileStream(fullPath,
                    FileMode.Open, FileAccess.ReadWrite, 
                    FileShare.None, 100))
                {
                    fs.ReadByte();

                    // If we got this far the file is ready
                    break;
                }
            }
            catch (Exception ex)
            {
                Log.LogWarning(
                   "WaitForFile {0} failed to get an exclusive lock: {1}", 
                    fullPath, ex.ToString());

                if (numTries > 10)
                {
                    Log.LogWarning(
                        "WaitForFile {0} giving up after 10 tries", 
                        fullPath);
                    return false;
                }

                // Wait for the lock to be released
                System.Threading.Thread.Sleep(500);
            }
        }

        Log.LogTrace("WaitForFile {0} returning true after {1} tries",
            fullPath, numTries);
        return true;
    }

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

1 голос
/ 22 сентября 2008

ОК, я работал над этим и закончил тем, что создал модуль стресс-теста, который в основном выбил из моего кода дерьмо из нескольких потоков ( См. Связанный вопрос ).

С этого момента стало намного легче находить дыры в моем коде. Оказывается, мой код на самом деле не был далеким, но был некоторый логический путь, по которому он мог бы войти, и который в основном вызывал сложение операций чтения / записи, то есть, если они не очищались вовремя, это иди бум!

Как только я снял это, снова запустил стресс-тест, все работало нормально!

Итак, я действительно ничего не делал специальный в своем коде доступа к файлу, просто гарантировал, что я использовал lock операторы, где это уместно (т.е. при чтении или записи ).

1 голос
/ 03 сентября 2008

Вы можете создать файл с временным именем («data.xml_TMP»), и когда он будет готов, измените имя на то, каким оно должно быть. Таким образом, никакой другой процесс не получит к нему доступ, пока он не будет готов.

1 голос
/ 03 сентября 2008

Если вы блокируете объект, сохраненный как static , тогда блокировка должна работать для всех потоков в одном домене приложения, но, возможно, вам нужно загрузить пример кода, чтобы мы могли посмотреть на оскорбительных линиях.

Тем не менее, одной мыслью было бы проверить, настроен ли IIS для работы в режиме Web Garden (то есть более одного процесса, выполняющего ваше приложение), который нарушил бы логику блокировки. Хотя вы могли бы исправить такую ​​ситуацию с помощью мьютекса, было бы проще перенастроить приложение для выполнения в одном процессе, хотя было бы целесообразно проверить производительность до и после работы с настройками веб-сада, поскольку это может повлиять производительность.

0 голосов
/ 21 сентября 2015

Как насчет использования AutoResetEvent для связи между потоками? Я создал консольное приложение, которое создает файл размером примерно 8 ГБ в методе createfile, а затем скопировал этот файл в методе main

 static AutoResetEvent waitHandle = new AutoResetEvent(false);
    static string filePath=@"C:\Temp\test.txt";
    static string fileCopyPath=@"C:\Temp\test-copy.txt";
    static void Main(string[] args)
    {
        Console.WriteLine("in main method");
        Console.WriteLine();
        Thread thread = new Thread(createFile);
        thread.Start();

        Console.WriteLine("waiting for file to be processed ");
        Console.WriteLine();
        waitHandle.WaitOne();
        Console.WriteLine();

        File.Copy(filePath, fileCopyPath);
        Console.WriteLine("file copied ");

    }


    static void createFile()
    {

        FileStream fs= File.Create(filePath);            
        Console.WriteLine("start processing a file "+DateTime.Now);
        Console.WriteLine();
        using (StreamWriter sw = new StreamWriter(fs))
        {
            for (long i = 0; i < 300000000; i++)
            {
                sw.WriteLine("The value of i is " + i);

            }
        }
        Console.WriteLine("file processed " + DateTime.Now);
        Console.WriteLine();

        waitHandle.Set();
    }
...