3

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

1)缓存中有数据并且数据没有过期(过时)——我们返回这个数据。非常快。2)缓存的数据过期了,但是另一个请求已经在查询数据库了。我们返回了缓存的数据。3)缓存数据过期,没有请求对数据库进行查询。该请求向前移动以进行查询。

但是,针对同名存储过程的数据库查询必须排队(要求)。

因此,我编写了这个队列类,旨在将这些查询排队并连续运行它们,而不是同时运行。这些队列类根据需要创建并存储在单例类的列表中。当请求移动到第 (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;
  }
}
4

2 回答 2

0

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

private object _lockObj=new object();
public ReportTable GetReportTable(ReportQuery query)
{
  lock(_lockObj){
    var table = query.GetNewReportTable();
    return table;
  }
}
于 2013-01-11T21:05:20.327 回答
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()。幸运的是,这里有一个很好的答案,可以很好地描述 Wait() 和 Pulse()。

关键是在collection改变之后Pulse(),给后续排队的线程一个机会来测试条件,也就是:我的查询是不是队列中的第一个?其他人是否已经在进行查询?如果测试失败,线程会调用 Wait(),将其放入等待队列,并且不会占用处理器周期。当前面的执行查询的线程完成,它将 _inUse 标志翻转为 false 并调用 Pulse(),唤醒下一个线程,以便它可以检查条件。

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

我已经对此进行了大量阅读,似乎 PulseAll() 在这种情况下可能是更好的调用,但到目前为止 Pulse() 正在工作,我们还没有遇到任何问题。

于 2013-01-12T00:39:56.233 回答