2009-04-09 4 views
12

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

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

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

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

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 является ложным.

Мои вопросы:

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

Edit: Подробнее

Так вернуться к этому утверждению: «Не беспокойтесь о том, что 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. Все элементы управления знают, как сделать себя превосходными, и вы можете посещать каждую созданную страницу самостоятельно (это «нормальный случай» на самом деле - отчет о превосходстве - это всего лишь совокупность всех порожденных страниц.)

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

ответ

2

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

+0

Al эта блокировка, вероятно, будет трудно устроить, исключение для потоковой передачи, которое он получает, относится к классу Page, который изменяет контекст http, если он не может переопределить действие на странице, которая делает это, и поместить блокировку, затем блокировка не будет работать. – meandmycode

+1

Отличный комментарий, я согласен с вами. Мое предпочтение состояло бы в том, чтобы передать совершенно новый объект, который извлекает часть своей информации из контекста HTTP во время создания протектора. Это было бы доказательством пули. –

4

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

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

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

1

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

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

1

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

Смежные вопросы