我在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;
}
}
答案 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()工作正常,我们没有遇到任何问题。