ASP MVC - Comet/Reverse Ajax/PUSH - Является ли этот код потокобезопасным?

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

Во-первых, у меня есть статическая переменная на моем контроллере, в которой хранится время последнего обновления данных:

public static volatile DateTime lastUpdateTime = 0;

Поэтому всякий раз, когда данные, которые я опрашиваю, изменяются, эта переменная будет изменена.

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

public ActionResult Push(DateTime lastViewTime)
{
    while (lastUpdateTime <= lastViewTime)
    { 
        System.Threading.Thread.Sleep(10000);
    }
    return Content("testing 1 2 3...");
}

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

Кажется, это работает нормально, но я беспокоюсь о безопасности потоков, это нормально? Нужно ли lastUpdateTime помечать как volatile? Есть ли способ лучше?

Спасибо

редактировать: возможно, мне следует использовать объект блокировки, когда я обновляю значение времени

private static object lastUpdateTimeLock = new object();

..

lock (lastUpdateTimeLock)
{
    lastUpdateTime = DateTime.Now;
}

person Paul Creasey    schedule 10.01.2010    source источник
comment
Пожалуйста, взгляните на этот проект ASP.NET Comet retkomma.wordpress.com/2009/09/05/исходный код для кометного проекта   -  person Amitd    schedule 11.03.2010


Ответы (2)


Что касается вашего первоначального вопроса, вы должны быть осторожны с DateTimes, поскольку они являются реальными объектами в среде выполнения .NET. Только несколько типов данных могут быть доступны изначально (например, int, bools) без блокировки (при условии, что вы не используете Interlocked). Если вы хотите избежать каких-либо проблем с Datetime, вы можете получить тики как long и использовать класс Interlocked для управления ими.

Тем не менее, если вы ищете кометные возможности в приложении .NET, вам, к сожалению, придется пойти намного дальше, чем то, что у вас есть здесь. IIS/ASP.NET не будет масштабироваться с тем подходом, который вы используете прямо сейчас; вы достигнете лимита еще до того, как наберете 100 пользователей. Помимо прочего, вам придется переключиться на использование асинхронных обработчиков и реализовать собственный ограниченный пул потоков для входящих запросов.

Если вам действительно нужно проверенное решение для ASP.NET/IIS, проверьте WebSync, это полная комета сервер, созданный специально для этой цели.

person jvenema    schedule 11.01.2010
comment
Возможно, было бы более разумно иметь таймер JavaScript, который вызывает службу через ajax каждые несколько минут? Я думаю, что задержка на пару минут была бы вполне разумной для приложения. - person Paul Creasey; 12.01.2010
comment
Я должен добавить, что этот сайт никогда не будет сильно загружен, возможно, максимум 15 пользователей одновременно. - person Paul Creasey; 12.01.2010
comment
Если задержка в несколько минут приемлема, то да, это прекрасное решение, особенно если учесть, что вы не имеете дело со слишком большим количеством пользователей. Тем не менее, нужно быть осторожным с потоками, хотя вы определенно хотите использовать либо метод блокировки, который вы описали, либо метод тиков/блокировки, о котором я упоминал, или вы столкнетесь с проблемами параллелизма в какой-то момент, независимо от нагрузки. - person jvenema; 12.01.2010

Честно говоря, меня бы беспокоило количество открытых соединений и пустой цикл while. С соединениями у вас, вероятно, все в порядке, но я определенно хотел бы провести нагрузочное тестирование, чтобы быть уверенным.

Кажется, что while (lastUpdateTime <= lastViewTime) {} должен иметь Thread.Sleep(100) или что-то в этом роде. В противном случае я бы подумал, что это будет потреблять много циклов процессора без необходимости.

Блокировка не кажется мне нужной в районе lastUpdateTime = DateTime.Now, т.к. предыдущее значение не имеет значения. Если бы это было lastUpdateTime = lastUpdateTime + 1 или что-то в этом роде, тогда возможно так оно и было бы.

person David Hogue    schedule 10.01.2010
comment
ага, я тоже подумала засыпать нить в петлю, это было бы очень разумно. Мне интересно узнать, что произойдет, если два потока попытаются изменить lastUpdateTime одновременно, и как другие подходят к этому стилю обратного AJAX с длинным опросом. - person Paul Creasey; 11.01.2010
comment
Обычно это не проблема, если два потока устанавливают переменную. Один поток установит его, а другой поток перезапишет его позже. Вам понадобится блокировка, если новое значение основано на предыдущем значении или если вы проверяете значение, прежде чем что-то делать. Что касается обратного AJAX, у меня лично не так много опыта. Я слышал о методах, подобных вашему, и похоже, что он должен работать. Я также слышал, что есть несколько специально разработанных веб-серверов, предназначенных для обслуживания этих запросов вместо того, чтобы связывать IIS с открытыми HTTP-соединениями, поскольку он на самом деле не был предназначен для этого. - person David Hogue; 11.01.2010
comment
@ Дэвид, нужно много дополнительной работы, чтобы заставить другой сервер работать и хорошо интегрироваться (настройка, разные порты, часто разные языки, сумасшедшие API и обратные вызовы и т. д.), и, поскольку IIS действительно работает феноменально хорошо для этой цели, это делает большой смысл его использовать. Проблема в том, что у IIS плохая репутация, потому что люди пытаются его реализовать ... ну, как описано в OP (без обид, Пол). Этот метод довольно быстро выходит из строя, как упоминалось в моем ответе, но есть и другие методы, которые позволяют очень хорошо масштабировать IIS, и тогда интеграция становится простой. В любом случае, просто к сведению :) - person jvenema; 14.01.2010