我有一个非常基本的ASP.NET应用程序,但是如果我们很幸运而且我不需要,那么现在有太多代码可以发布。
我们有一个名为ReportGenerator
的班级。单击按钮,将调用方法GenerateReports。它使用InternalGenerateReports
对ThreadPool.QueueUserWorkItem
进行异步调用并返回,结束ASP.NET响应。它不提供任何完成回调或任何东西。
InternalGenerateReports
在线程池中创建并维护五个线程,每个线程一个报告,也使用QueueUserWorkItem
,通过“创建”五个线程,同时使用并等待所有这些线程的调用完成,一个循环。每个线程使用ASP.NET ReportViewer控件将报表呈现为HTML。也就是说,对于200个报告,InternalGenerateReports
应该创建5个线程40次。线程完成后,报告数据将排队,当所有五个文件完成后,报告数据将刷新到磁盘。
我最大的问题是,在运行一个报告后,aspnet进程被“挂起”,而且在大约200个报告中,应用程序就会挂起。
我只是将此代码简化为在单个线程中运行,这样可以正常工作。在我们进入像我的代码之类的细节之前,在上面的版本中有什么明显的可能是错误的吗?
以下是代码的简要示例:
public class SscceReports
{
Dictionary<Guid, AsyncReportCreator> runningWorkers = new Dictionary<Guid, AsyncReportCreator>();
public void GenerateReports(Queue<int> reportKeys)
{
int goodPoolSize = System.Environment.ProcessorCount;
System.Threading.ThreadPool.SetMaxThreads(goodPoolSize + 10, goodPoolSize * 10);
System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(InternalGenerateReports), reportKeys);
}
void InternalGenerateReports(object state)
{
Queue<int> reportKeys = state as Queue<int>;
while (reportKeys.Count > 0)
{
for (int i = 0; i < 5 && reportKeys.Count > 0; i++)
{
Guid workerId = Guid.NewGuid();
int rk = (int) reportKeys.Dequeue();
AsyncReportCreator asrc = new AsyncReportCreator(rk);
runningWorkers.Add(workerId, asrc);
asrc.WorkComplete += CompleteCallBack;
System.Threading.ThreadPool.QueueUserWorkItem(asrc.StartWork);
}
while (runningWorkers.Count > 0)
System.Threading.Thread.Sleep(500);
}
while (runningWorkers.Count > 0)
System.Threading.Thread.Sleep(5000);
}
void CompleteCallBack(object state)
{
// Write queued report content to disk.
runningWorkers.Remove((Guid) state);
}
}
public class AsyncReportCreator
{
public event System.Threading.WaitCallback WorkComplete;
private int key;
public AsyncReportCreator(int reportKey)
{
key = reportKey;
}
public void StartWork(object state)
{
// Create report;
WorkComplete(state);
}
}
答案 0 :(得分:0)
.NET ThreadPool
默认为25 threads per processor,因此如果您在致电InternalGenerateReports
时生成200份报告,则会在ThreadPool
中排队1,000个工作项( 200个报告*每个InternalGenerateReports
5个工作项。一次只能激活25个,但您可以看到此模型可能不适合您。
线程池的默认限制为 每个可用处理器25个线程, 可以使用改变 CorSetMaxThreads中的定义 mscoree.h文件。每个线程使用 默认堆栈大小并在 默认优先级。每个过程都可以 只有一个操作系统线程 池。
考虑使用生产者/消费者模式,而不是排队25个工作项(即25个线程),只需创建一些消费者线程(匹配您拥有的处理器/核心数),从中央处理报告生成请求阻塞队列由生产者填充。那会让事情变得更合理......
有some articles表示不应在ASP.NET中使用ThreadPool
:
您可以使用ThreadPool 在ASP.NET和它中完全相同的方式 像你期望的那样工作。该 问题不在ThreadPool中 本身,但在ASP.NET使用的其他东西 这是为了同时。 ASP.NET是 多线程的设计和使用 ThreadPool用于提供页面和 映射到ASP.NET ISAPI的内容 过滤
如果你也使用ThreadPool,那么 ASP.NET使用的线程较少 和请求被搁置,直到 pool返回一个免费线程。这有可能 对于低流量而言,这不是问题 网站,但更受欢迎的网站可以得到 陷入麻烦。低流量站点可以 如果他们使用的话会遇到麻烦 ThreadPool很多。
我认为我发现了你的问题......你正在排队asrc.StartWork
但是你并没有传递state
所以当调用CompleteCallBack
时它没有任何内容从runningWorkers
中删除。这是一个改进的版本:
public class CountDownLatch {
private volatile int m_remain;
private EventWaitHandle m_event;
public CountDownLatch(int count) {
m_remain = count;
m_event = new ManualResetEvent(false);
}
public void Signal() {
// The last thread to signal also sets the event.
if (Interlocked.Decrement(ref m_remain) == 0)
m_event.Set();
}
public void Wait() {
m_event.WaitOne();
}
}
public class SscceReports
{
public void GenerateReports(Queue<int> reportKeys)
{
int goodPoolSize = System.Environment.ProcessorCount;
System.Threading.ThreadPool.SetMaxThreads(goodPoolSize + 10, goodPoolSize * 10);
System.Threading.ThreadPool.QueueUserWorkItem(o=>
{
InternalGenerateReports(reportKeys);
});
}
void InternalGenerateReports(Queue<int> reportKeys)
{
// assuming that the reportKeys.Count contains only 5 keys,
// we create a countdown latch to hande the same number of keys
CountDownLatch latch = new CountDownLatch(reportKeys.Count);
foreach( int rk in reportKeys)
{
AsyncReportCreator asrc = new AsyncReportCreator(rk);
asrc.WorkComplete += CompleteCallBack;
System.Threading.ThreadPool.QueueUserWorkItem(o=>
{
asrc.StartWork(latch);
});
}
// Wait for all the tasks to complete instead of sleeping
latch.Wait(); // <- blocks untill all (5) tasks call latch.Signal()
}
void CompleteCallBack(CountDownLatch latch)
{
// Write queued report content to disk.
latch.Signal();
}
}
public class AsyncReportCreator
{
public delegate void WorkComplete(CountDownLatch latch);
public AsyncReportCreator(int reportKey)
{
key = reportKey;
}
public void StartWork(CountDownLatch latch)
{
// Create report;
WorkComplete(latch);
}
}
答案 1 :(得分:0)
如果您正在使用,或者可以使用Framework 4.0,那么新的Parallel扩展非常好,并且对于并行处理非常简单。我将它与BackgroundWorker类结合起来创建了非常强大但简单的处理引擎:
http://msdn.microsoft.com/en-us/library/dd460720.aspx
比自己处理所有线程容易得多。这可以很好地扩展。