Очередь FIFO приводит к тому, что процессор IIS достигает максимума в 100%


У меня есть одноэлементный класс службы в IIS как часть веб-приложения (служба должна быть одноэлементной по причинам кэширования данных). Клиенты браузера, которые делают запросы к сервису, могут получить один из трех результатов:

1) в кэше есть данные, и данные не просрочены (устаревшие) - мы возвращаем эти данные. Очень быстрый. 2) Срок действия кэшированных данных истек, но другой запрос уже запрашивает базу данных. Мы вернули кэшированные данные. 3) срок хранения кэшированных данных истек, и запросы не выполняются. выполнение запросов к БД. Этот запрос перемещается вперед, чтобы сделать запрос.

Однако запросы базы данных, нацеленные на хранимые процедуры с тем же именем, должны быть помещены в очередь (требование).

Таким образом, я написал этот класс queue, который предназначен для того, чтобы помещать эти запросы в очередь и запускать их последовательно, а не одновременно. Эти классы очередей создаются по мере необходимости и хранятся в списке в классе singleton. Когда запрос перемещается в Часть (3), он находит класс очереди, соответствующий его сохраненному имя процедуры и отправляет запрос в класс queue. Затем он ожидает, пока данные не будут возвращены из БД, чтобы он мог обслуживать HTML-запрос.

К сожалению, после нескольких часов работы с этим кодом серверный процесс работает на 100%.

Я не уверен, что лучший способ-это улучшить его, потому что многопоточное кодирование-не моя специальность.

Код класса очереди выглядит следующим образом:

public ReportTable GetReportTable(ReportQuery query)
{
  lock (_queue)
  {
    _queue.Enqueue(query);
    Monitor.Pulse(_queue);
  }

  lock (_queue)
  {
    var firstQueryInQueue = _queue.Peek();
    while (_inUse || firstQueryInQueue == null || firstQueryInQueue.GetHashCode() != query.GetHashCode())
    {
      Monitor.Pulse(_queue);
      Monitor.Wait(_queue);
    }

    _inUse = true;
    firstQueryInQueue = _queue.Dequeue();
    var table = firstQueryInQueue.GetNewReportTable();
    _inUse = false;

    Monitor.Pulse(_queue);
    return table;
  }
}
2 3

2 ответа:

Я не знаю, понимаю ли я проблему. но вы можете переписать его очень просто

private object _lockObj=new object();
public ReportTable GetReportTable(ReportQuery query)
{
  lock(_lockObj){
    var table = query.GetNewReportTable();
    return table;
  }
}

Так вот что я сделал, чтобы исправить это.

public ReportTable GetReportTable(ReportQuery query)
{
  lock (_queue)
  {
    _queue.Enqueue(query);
    Monitor.Pulse(_queue);
  }

  lock (_queue)
  {
    var firstQueryInQueue = _queue.Peek();
    while (_inUse || firstQueryInQueue == null || firstQueryInQueue.GetHashCode() != query.GetHashCode())
    {
      Monitor.Wait(_queue);
    }

    _inUse = true;
    firstQueryInQueue = _queue.Dequeue();
    var table = firstQueryInQueue.GetNewReportTable();
    _inUse = false;

    Monitor.Pulse(_queue);
    return table;
  }
}

Причина, по которой он не работал раньше, заключалась в том, что у меня не было полного понимания монитора.Wait () и монитор.Пульс(). Я использовал Pulse () в неправильном месте кода. К счастью, есть очень хороший ответ здесь, который довольно хорошо описывает Wait() и Pulse ().

Ключ должен пульсировать () после того, как коллекция изменена, и дать последующим потокам в очереди шанс проверить условие, которое является: is my запрос первый в очереди? И кто-то еще уже делает запрос? Не пройдя этот тест, поток вызывает функцию Wait (), которая помещает его в очередь ожидания и не связывает процессорные циклы. Когда поток, который находится впереди и выполняет запрос, завершается, он переворачивает флаг _inUse на false и вызывает Pulse (), пробуждая один из следующих потоков, чтобы он мог проверить условие.

После внедрения этого решения и просмотра Диспетчера задач в течение дня, я был рад видеть 1% к 5% нагрузки на сервер в течение нескольких часов, и процессор никогда не поднимался до 100%, как раньше.

Я много читал об этом, и кажется, что PulseAll() может быть лучшим вызовом в этом сценарии, но пока Pulse() работает, и мы не столкнулись ни с какими проблемами.