Блокировка, используемая в обратном вызове элемента кэша, а другой метод, похоже, не блокирует - PullRequest
1 голос
/ 29 октября 2009

Самое простое объяснение, которое я могу дать:

В моем веб-приложении .NET1.1 я создаю файл на диске в методе Render и добавляю элемент в Cache, срок действия которого истекает, скажем, через минуту. У меня также есть метод обратного вызова, который вызывается по истечении срока действия элемента кэша, который удаляет файл, созданный с помощью Render. В методе Page_Init я пытаюсь получить доступ к файлу, который метод Render записал на диск. Оба эти метода имеют оператор блокировки, блокирующий закрытый статический объект.

Намерение:

Создать страницу, которая, по сути, записывает свою копию на диск, которая удаляется до того, как станет слишком старой (или устаревшей по содержанию), при обслуживании файла, если он существует на диске.

Наблюдаемая проблема:

Это действительно две проблемы, я думаю. Запрос страницы делает то, что я ожидаю, она рендерит страницу на диск и обслуживает ее немедленно, добавляя элемент срока действия в кеш. Для тестирования время истечения составляет 1 минуту.

Затем я ожидаю, что метод обратного вызова будет вызван через 60 секунд и удалит файл. Это не так.

Еще через минуту (ради аргумента) я обновляю страницу в браузере. Затем я могу увидеть, как вызывается метод обратного вызова, и установить блокировку на объект блокировки. Page_Init также вызывается и устанавливает блокировку на тот же объект. Однако оба метода, по-видимому, вводят свой блок кода блокировки и продолжают выполнение.

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

Ужасно упрощенный фрагмент кода:

public class MyPage : Page
{
  private static Object lockObject = new Obect();

  protected void Page_Init(...)
  {
    if (File.Exists(...))
    {
      lock (lockObject)
      {
        if (File.Exists(...))
        {
          Server.Transfer(...);
        }
      }
    }
  }

  protected override void Render(...)
  {
    If (!File.Exists(...))
    {
      // write file out and serve initial copy from memory
      Cache.Add(..., new CacheItemRemovedCallback(DoCacheItemRemovedCallback));
    }
  }

  private static void DoCacheItemRemovedCallback(...)
  {
    lock (lockObject)
    {
      If (File.Exists(...))
        File.Delete(...);
    }
  }
}

Может кто-нибудь объяснить это, пожалуйста? Я понимаю, что метод обратного вызова, по сути, является ленивым и, следовательно, вызывает только один раз, когда я делаю запрос, но, конечно, многопоточность в .NET1.1 достаточно хороша, чтобы не допустить одновременного входа двух блоков lock ()?

Спасибо

Мт.

Ответы [ 2 ]

1 голос
/ 29 октября 2009

Не уверен, почему ваше решение не работает, но это может быть хорошо, учитывая последствия ...

Я бы предложил совершенно другой маршрут. Отделите процесс управления файлом от процесса запроса файла.

Запросы следует просто перейти в кэш, получить полный путь к файлу и отправить его клиенту.

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

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

0 голосов
/ 29 октября 2009

Первое, что я хотел бы сделать, это открыть окно Threads и посмотреть, на каком потоке работает Page_Init, а на каком потоке работает Call Back. Единственный способ узнать, что два метода могут заблокировать один и тот же объект, - это если они работают в одном потоке.

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

Реальная проблема здесь в том, как на самом деле работает Server.Transfer. Server.Transfer просто настраивает некоторые внутренние детали ASP.NET, указывая на то, что запрос будет перенесен на другой URL-адрес на сервере. Затем он вызывает Response.End, который, в свою очередь, создает исключение ThreadAbortException. На данный момент фактические данные не были прочитаны или отправлены клиенту.

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

Теперь где-то глубоко внутри ASP.NET исключение ThreadAbortException обрабатывается каким-то образом и обрабатывается запрос на новый URL-адрес. В это время он обнаружил, что файл пропал.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...