这是使用Threadpool的正确案例吗?

时间:2009-11-17 12:25:15

标签: c# winforms invoke threadpool

以下是设置:我正在尝试制作一个相对简单的Winforms应用程序,一个使用FeedDotNet库的提要阅读器。我的问题是关于使用线程池。由于FeedDotNet正在进行同步HttpWebRequests,因此它阻止了GUI线程。所以最好的事情似乎是将同步调用放在ThreadPool线程上,当它工作时,调用需要在表单上更新的控件。一些粗略的代码:

private void ThreadProc(object state)
{
 Interlocked.Increment(ref updatesPending);

    // check that main form isn't closed/closing so that we don't get an ObjectDisposedException exception
 if (this.IsDisposed || !this.IsHandleCreated) return;
 if (this.InvokeRequired)
  this.Invoke((MethodInvoker)delegate
  {
   if (!marqueeProgressBar.Visible)
    this.marqueeProgressBar.Visible = true;
  });

 ThreadAction t = state as ThreadAction;
 Feed feed = FeedReader.Read(t.XmlUri);

 Interlocked.Decrement(ref updatesPending);

 if (this.IsDisposed || !this.IsHandleCreated) return;
 if (this.InvokeRequired)
  this.Invoke((MethodInvoker)delegate { ProcessFeedResult(feed, t.Action, t.Node); });

 // finished everything, hide progress bar
 if (updatesPending == 0)
 {
  if (this.IsDisposed || !this.IsHandleCreated) return;
  if (this.InvokeRequired)
   this.Invoke((MethodInvoker)delegate { this.marqueeProgressBar.Visible = false; });
 }
}

this =主表单实例

updatesPending =主窗体中的volatile int

ProcessFeedResult =对Feed对象执行某些操作的方法。由于线程池线程无法返回结果,这是通过主线程处理结果的可接受方式吗?

我担心的主要问题是它如何扩展。我一次尝试了大约250个请求。我看到的最大线程数大约为53,一旦完成所有线程,就会回到21.我记得在一个特殊的例子中我玩代码,我看到它上升到120.这不是'是正常的,是吗?此外,在Windows XP上,我认为如此大量的连接,某个地方会出现瓶颈。我对吗?

我可以做些什么来确保线程/连接的最高效率?

拥有所有这些问题也让我想知道这是否适合Threadpool使用。 MSDN和其他消息人士称,它应该用于“短期”任务。考虑到我的连接速度相对较快,1-2秒“足够短暂”了吗?如果用户使用56K拨号,一个请求可能需要5-12秒甚至更长时间,该怎么办?那么线程池也是一种有效的解决方案吗?

5 个答案:

答案 0 :(得分:2)

我对ThreadPool的理解是它是针对这种情况而设计的。我认为短命的定义是这个时间顺序 - 甚至可能达到几分钟。一个“长寿”的线程将是一个在应用程序的生命周期中存活的线程。

不要忘记微软会花一些时间尽可能高地提高ThreadPool的效率。你认为你能写出更高效的东西吗?我知道我不能。

答案 1 :(得分:2)

ThreadPool,未经检查可能是一个坏主意。

开箱即用,您在线程池per cpu中获得250个线程。

想象一下,如果在一次爆发中你弄平了某人的网络连接并让他们被禁止从某个网站收到通知,因为他们被怀疑是在运行DoS攻击。

相反,当从网上下载东西时,你应该建立大量的控制权。用户应该能够决定他们发出多少并发请求(以及每个域的并发请求数),理想情况下,您还希望提供带宽量的控制。

虽然可以使用ThreadPool进行编排,但使用专用线程或使用类似BackgroundWorker类的一堆实例的东西是更好的选择。

答案 2 :(得分:2)

.NET线程池专门用于执行短期运行的任务,创建新线程的开销会抵消创建新线程的好处。它不适用于长时间阻塞或执行时间较长的任务。

这个想法是让任务跳到一个线程,快速运行,完成并跳下去。

BackgroundWorker类提供了一种在线程池线程上执行任务的简便方法,并提供了任务报告进度和处理取消请求的机制。

在BackgroundWorker组件的this MSDN article中,文件下载是作为适当使用此类的示例明确给出的。这应该鼓励你使用这个课来完成你需要的工作。

如果您担心过度使用线程池,可以确保运行时根据需求管理可用线程的数量。任务在线程池上排队等待执行。当线程可用于执行工作时,任务将加载到线程上。监视进程定期检查线程池的状态。如果有任务等待执行,它可以创建更多线程。如果有多个空闲线程,它可以关闭一些以释放资源。

在更糟糕的情况下,如果所有线程都忙,并且您的工作排队,则运行时将添加线程来处理额外的工作负载。应用程序将运行得更慢,因为它必须等待更多线程可用,但它将继续运行。

答案 3 :(得分:2)

几点,并将信息与其他一些答案相结合:

  • 您的ThreadProc不包含异常处理。您应该添加或1 I / O错误将停止您的过程。
  • Sam Saffron是正确的,你应该限制线程数。您可以使用(ThreadSafe)队列将您的订阅源推送到(WorkItems),并在循环中从队列中读取1个以上的线程。
  • BackgrounWorker可能是一个好主意,它会为您提供所需的异常处理和同步。
  • BackgrounWorker使用ThreadPool,没关系

答案 4 :(得分:1)

您可能需要查看"BackgroundWorker"课程。