FIFO队列导致IIS处理器最大值达到100%

时间:2013-01-10 15:00:45

标签: c# multithreading iis queue fifo

我在IIS上有一个单独的服务类作为Web应用程序的一部分(该服务是数据缓存的单例)。向服务发出请求的浏览器客户端可以有以下三种结果之一:

1)缓存中有数据且数据未过期(陈旧) - 我们返回此数据。非常快。 2)缓存的数据已过期,但另一个请求已经在查询数据库。我们返回了缓存的数据。 3)缓存的数据已过期,没有请求向DB发出查询。该请求向前移动以进行查询。

但是,必须对具有相同名称的目标存储过程的数据库查询进行排队(要求)。

因此,我编写了这个队列类,用于对这些查询进行排队并连续运行它们,而不是并发运行。这些队列类根据需要创建并存储在singleton类的列表中。当请求移动到part(3)时,它会找到与其存储过程名称匹配的队列类,并将请求提交给队列类。然后等待直到从数据库返回数据,以便它可以为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 个答案:

答案 0 :(得分:0)

我不知道我是否理解问题 但你可以非常简单地重写它

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

答案 1 :(得分:0)

所以这就是我所做的修复它。

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;
  }
}

之前没有用的原因是因为我对Monitor.Wait()和Monitor.Pulse()缺乏全面的了解。我在代码中的错误位置使用Pulse()。幸运的是,有一个很好的答案here可以很好地描述Wait()和Pulse()。

更改集合后,关键是Pulse(),并为后续排队的线程提供测试条件的机会,即:我的查询是否在队列中首先?是否有其他人已经在进行查询?如果测试失败,则线程调用Wait(),将其置于等待队列中,并且不会占用处理器周期。当前面 的线程正在执行查询时,它会将_inUse标志翻转为false并调用Pulse(),唤醒下一个线程之一,以便检查条件。

在实施此解决方案并观察任务管理器一天之后,我很高兴看到服务器上有1%到5%的负载持续了几个小时,并且CPU从未像以前那样攀升到100%。

我已经对此做了很多阅读,看起来PulseAll()可能是这种情况下更好的调用,但到目前为止Pulse()工作正常,我们没有遇到任何问题。