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

Использование HTTPContext по потокам

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

 ((System.Web.IHttpHandler)instance).ProcessRequest(reference to spawn 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 и передаю его в другие потоки, и что он не Thread-Safe.

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

Мои вопросы:

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

Изменить: Подробнее

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

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

 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 - это всего лишь совокупность всех порожденных страниц.)

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

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

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

Ответ 4

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