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

StackOverflow https://stackoverflow.com/questions/41290

Вопрос

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

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

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

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

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

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

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

Это было полезно?

Решение

Вот код, который я использую, чтобы убедиться, что файл не заблокирован другим процессом.Он не на 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-файлов, запись которых занимает некоторое время.

Другие советы

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

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

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

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

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

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

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

Как насчет использования 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();
    }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top