Использование HTTPContext между потоками - PullRequest
13 голосов
/ 09 апреля 2009

Пользователь переходит на страницу spawn.aspx, которая порождает полдюжины потоков, отображая страницы, используя

 ((System.Web.IHttpHandler)instance).ProcessRequest(reference to spawn's HTTPContext);

Не беспокойтесь о том, что ASP.Net, по-видимому, отправляет пользователю 7 ответов на 1 запрос, эта часть обрабатывается и отправляется только один ответ.

Проблема в том, что в среде с высоким трафиком (наша среда производства) со многими потоками (четырехугольниками) мы получаем ошибку:

System.IndexOutOfRangeException 
at System.collections.ArrayList.Add 
at System.Web.ResponseDependencyList.AddDependencies(String[] items, String argname, Boolean cloneArray, DateTime utcDepTime) 
at System.Web.ResponseDependencyList.AddDependencies(String[] items, String argname, Boolean cloneArray, String requestVritualPath)
at System.Web.UI.Page.AddWrappedFileDependencies(Object virtualFileDependencies) 
at ASP.spawned_page_no_1_aspx.FrameworkInitialize()
at System.Web.UI.Page.ProcessRequest

Мы не можем дублировать это в другом месте. Мой коллега считает, что это потому, что я повторно использую оригинальный HTTPContext и передаю его в другие потоки, и что он не является поточно-безопасным.

Следуя этой логике, я попытался создать новый HTTPContext для передачи в потоки. Но, похоже, части этого не будут «объединяться». В частности, мне нужно получить объект Session в новый HTTPContext. Я полагаю, я бы хотел добавить и другие части, такие как Кэш. Для записи HTTPContext.Current.Session.IsSynchronized имеет значение false.

Мои вопросы:

  1. Считаете ли вы, что ошибка связана с использованием HTTPContext между потоками?
  2. Как я могу это исправить?
  3. Если исправление дублирует HTTPContext для каждого потока, как я могу получить сеанс (и кэш) в новом? Запрос и ответ приходят в ctor, но сеанс не устанавливается.

Редактировать: Подробнее

Итак, возвращаясь к этому утверждению: «Не беспокойтесь о том, что ASP.Net, похоже, отправляет пользователю 7 ответов на 1 запрос, эта часть обрабатывается и отправляется только один ответ». Огромный поклонник Рэймонда Чена, я согласен с вами: «Теперь у вас две проблемы» - разумное утверждение в отсутствие какой-либо дополнительной информации.

На самом деле происходит то, что я создаю документ Excel для отправки обратно. На странице spawn.aspx настраивается некоторая информация о состоянии, в том числе тот факт, что он рендерится в excel, и объект, для которого выполняется рендеринг. Каждая порожденная страница получает эту информацию и блокируется до тех пор, пока не наступит их очередь визуализации объекта. Если буквально выглядит так:

 protected override void Render(System.Web.UI.HtmlTextWriter writer)
 {
    if (this.RenderToExcel)
    {
      Deadlocker.SpinUntilCurrent(DeadLockToken);
      RenderReport(this, this.XLSWriter);
      Deadlocker.Remove(DeadLockToken);
    }
    else
      base.Render(writer);
 }

Но вся обработка до этого момента - доступ к базе данных, управление иерархией, все это выполняется параллельно. И его много - достаточно того, что его упорядочение, в то же время позволяя блокировать его в Render, сократит общее время более чем вдвое.

И лучшая его часть - ничего не нужно было переписывать для рендера Excel. Все элементы управления знают, как сделать себя лучше, и вы можете посещать каждую порожденную страницу независимо (на самом деле это «нормальный случай» - отчет Excel - это просто совокупность всех порожденных страниц.)

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

Ответы [ 4 ]

4 голосов
/ 10 апреля 2009

Хотя HttpContext предназначен для обработки контекста, который не является специфичным для потока (поскольку контекст http может начинаться в одном потоке и заканчиваться в другом), он не является неявно поточно-ориентированным.

По сути, проблема в том, что вы делаете что-то, что не предназначено, эти запросы обычно бывают множественными, и у каждого есть свое назначенное HttpApplication для выполнения запроса, и у каждого есть свой собственный HttpContext.

Я бы действительно попытался позволить инфраструктуре asp.net делегировать запросы самостоятельно.

2 голосов
/ 09 апреля 2009

Ваши коллеги правы: если один поток блокирует ресурс, а другой пытается его использовать, то ваш пул потоков становится бумом! Не очень хороший результат. Большинство людей решают эту проблему, создавая новые объекты и передавая их в параматизированные потоки. Если вам абсолютно необходимо использовать один и тот же объект, вы должны реализовать некоторый код, который сначала проверяет, используется ли ресурс другим потоком, а затем немного подождет, прежде чем проверять снова. Примером может быть создание bool IsInUse, который вы всегда проверяете сначала, а затем ваш поток устанавливает его в значение true, если он использует этот ресурс, а затем в false, когда это делается, предотвращая попытки других потоков использовать основной ресурс (ваш httpContext) , Надеюсь, это поможет.

1 голос
/ 27 февраля 2011

Я бы гарантировал, что каждый раз, когда вы обращаетесь к коллекции HttpContext.Current.Items, вы используете блок блокировки монитора (C #) / SyncLock (VB) для объекта HttpContext.Current.Items.SyncRoot, чтобы обернуть ваши вызовы.

1 голос
/ 11 апреля 2009

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

Есть ли способ, которым вы можете отделить операции, которые вы должны выполнять параллельно с HttpContext? Если они просто загружают данные и записывают в формат CSV, этот код не имеет необходимой зависимости от пользовательского элемента управления ASP.NET или жизненного цикла страницы. После удаления этой зависимости вы можете реализовать страницу с помощью асинхронного HttpHandler, выполняя параллельные операции во время между IHttpHandler.BeginProcessingRequest () и IHttpHandler.EndProcessingRequest ().

...