Доступ к HttpContext.Current из разных потоков

У меня есть приложение C# ASP.NET, которое запускает около 25 различных потоков, выполняющих некоторые методы в классе SiteCrawler.cs.

В HttpContext.Current.Session я хочу сохранить результат поиска, сделанный пользователем, и представить его пользователю, когда все потоки будут завершены. Моя проблема в том, что объект HttpContext.Current имеет значение null в порожденных потоках, потому что его там нет.

Какие еще варианты у меня есть для сохранения конкретных данных пользователя/сеанса без использования сеанса из-за ограничений, когда приложение является многопоточным?

Я пытался найти решение на каждом дюйме Stackoverflow, но безуспешно....


person Raydk    schedule 19.01.2012    source источник
comment
Я думаю, вы всегда можете передать текущий HttpContext при создании новых потоков и обновлении сеанса в этом.   -  person musefan    schedule 19.01.2012
comment
Разве нельзя запустить 25 потоков, собрать результаты в потоке asp.net и затем сохранить полные результаты в сеансе?   -  person Wouter de Kort♦    schedule 19.01.2012
comment
Порождать потоки для «длительных процессов», как правило, плохая идея в веб-приложении.   -  person Grant Thomas    schedule 19.01.2012
comment
musefan — я думал об этом, но не уверен, что это хорошая идея, потому что порожденный поток изменит содержимое переданного в HttpContext, которое к тому времени также будет другим.   -  person Raydk    schedule 19.01.2012
comment
Воутер де Корт: Как бы вы подошли к этому?   -  person Raydk    schedule 19.01.2012
comment
Я искал решение связанной с этим проблемы, и кажется, что правильный способ протекания HttpContext — это испачкать руки SynchronizationContext. Вместо того, чтобы вдаваться в подробности, я просто укажу вам на этот тщательный и хорошо написанный пост в блоге, который отвечает на ваш вопрос о HttpContext, а также в более сложных сценариях многопоточности. В нем он упоминает классический пост Стивена Туба [ExecutionContext vs SynchronizationContext](http   -  person Mark    schedule 28.02.2019
comment
@GrantThomas Серьезный вопрос: у Chrome есть (слишком короткий) тайм-аут для запросов, которые мы не можем контролировать (на самом деле это очень непоследовательно). Как тогда вы предлагаете структурировать вызов долго выполняющегося запроса?   -  person David I. McIntosh    schedule 23.12.2019


Ответы (5)


В моем приложении много кода, использующего HttpContext.Current, и я не могу изменить этот код.

worker.DoWork() из приведенного ниже примера использует этот код. И мне пришлось запустить его в отдельном потоке.

Я пришел к следующему решению:

 HttpContext ctx = HttpContext.Current;
 Thread t = new Thread(new ThreadStart(() =>
                {
                    HttpContext.Current = ctx;
                    worker.DoWork();
                }));
 t.Start();
 // [... do other job ...]
 t.Join();
person Dmitry Andrievsky    schedule 11.10.2013
comment
вау спасибо ты меня спас! Интересно, как это повлияет на эту память, если я буду использовать ее больше - person CMS; 01.11.2015
comment
Обратите внимание, что в целом это плохой поступок. Например, иногда вы можете обнаружить, что HttpContext.Current.Items[x] возвращает null вместо введенного вами значения, потому что ASP очистил ресурсы после завершения HTTP-запроса. Это быстрый и грязный подход, который, вероятно, будет работать большую часть времени, но не полагайтесь на него для чего-то важного. - person Rory; 30.12.2015
comment
Я не согласен с точкой зрения @Rory. Я думаю, что это самое элегантное решение для этого, если сравнивать его с другими вариантами. Прежде всего, вы не клонируете HttpContext, а просто передаете ссылку. Поскольку Thread может работать дольше, чем сам HttpRequest, можно легко проверить, является ли HttpContext нулевым, прежде чем обращаться к нему. В качестве улучшения приведенного выше фрагмента кода передача ctx также может быть выполнена через WeakReference — таким образом поток не будет препятствовать сбору мусора HttpContext при необходимости. Опять же, в зависимости от жизненного цикла, который вам нужен, вы корректируете приведенный выше код в соответствии со своими потребностями. + Принятый ответ - person AlexVPerl; 03.02.2016
comment
@Rory, который подразумевает асинхронный вызов типа «выстрелил-забыл», и это — плохая идея. Если вы не делаете ничего странного и убедитесь, что веб-запрос ожидает завершения всех асинхронных вызовов, все должно быть в порядке, верно? - person user247702; 05.07.2016
comment
Я думаю, что не заметил вызова t.Join() в этом ответе, когда писал свой предыдущий комментарий. Я согласен, что должно быть в порядке, если фоновые потоки завершатся до http-запроса. По моему опыту, проблемы возникали, когда сначала завершался веб-запрос, а фоновая задача все еще выполнялась. Обратите внимание, что проблема НЕ в том, что HttpContext получает gc-ed - явно есть ссылка, поэтому ее не будет, - но что-то в ASP.NET удаляет все элементы в HttpContext.Current, поэтому, если вы ожидаете, что что-то будет в этой коллекции не повезло тебе. - person Rory; 05.07.2016
comment
@MinhNguyen вам нужно было получить доступ к HttpContext.Current.Session в вашем случае? Используя этот подход, я могу получить HttpContext.Current, но не HttpContext.Current.Session. - person Don Cheadle; 21.07.2016
comment
помог мне использовать параллельную библиотеку в приложении asp.net с сеансом. - person Dinesh Kumar; 23.01.2018
comment
Как я могу установить HttpContext.Current, если я внедряю HttpContextBase в свой класс? - person Vin Shahrdar; 11.06.2018
comment
Я столкнулся с аналогичным ограничением и использовал FakeHttpContext, который сериализуется (я фиксирую все переменные сеанса), а затем восстанавливается в этот FakeHttpContext (например, этот - person drizin; 24.11.2019

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

Также К. Скотт Аллен опубликовал немного более короткую статью по этому вопросу: Работа с HttpContext.Current

person Dennis Traub    schedule 19.01.2012
comment
Эта статья «Работа с HttpContext.Current» была жизненно важна, чтобы помочь мне с моей конкретной проблемой, спасибо. - person mungflesh; 07.09.2015

@Rory сделал комментарий выше, что некоторые объекты в HttpContext станут нулевыми, даже если вы передадите их в поток. Это случилось со мной со свойством User. Поэтому вместо этого вы можете скопировать пользователя в поток CurrentPrincipal следующим образом:

В контексте контроллера сохраните пользователя:

            _user = HttpContext.Current.User;
            var processThread = new Thread(() => ThreadedCode());
            processThread.Start();

В потоке установите пользователя «Поток»:

    private static void ThreadedCode()
    {
        // Workaround for HttpContext.Current.User being null.
        // Needed for CreatedBy and RevisedBy.
        Thread.CurrentPrincipal = _user;

Обратите внимание, что HttpContext будет доступен только на время существования запроса. Поток будет жить потенциально намного дольше, чем запрос, возможно, именно поэтому вам в первую очередь нужен поток! :)

person Jess    schedule 23.02.2016
comment
@Rory, спасибо за ваш комментарий выше. Это заставило меня задуматься об этом альтернативном ответе. - person Jess; 24.02.2016

Просто добавьте HttpContext.Current в конструктор вашего класса SiteCrawler.cs.

public class SiteCrawler
{
     HttpContext context = HttpContext.Current;

    public void Method()
    {
        context.WhateverYouWant
    }
}
person jjspierx    schedule 17.02.2015

Вы можете сохранить его в базе данных, а затем вы можете позволить браузеру пользователя обновляться или использовать ajax или использовать новый сигнализатор, чтобы проверить, записан ли результат уже в db. Надеюсь, поможет.

person ysrb    schedule 19.01.2012
comment
Сохранение переменных сеанса в базе данных не будет проблемой. Я просто не знаю, как лучше всего связать его с правильным сеансом и получить его, когда потоки будут выполнены. - person Raydk; 19.01.2012