【问题标题】:Is this a right case for using the Threadpool?这是使用线程池的正确案例吗?
【发布时间】:2009-11-17 21:04:06
【问题描述】:

设置如下:我正在尝试制作一个相对简单的 Winforms 应用程序,一个使用 FeedDotNet 库的提要阅读器。我的问题是关于使用线程池。由于 FeedDotNet 正在生成同步 HttpWebRequest,因此它阻塞了 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 秒甚至更多时间。那么线程池也会是一种有效的解决方案吗?

【问题讨论】:

  • 还请原谅我不停地喋喋不休,我还是个新手。

标签: c# winforms invoke threadpool


【解决方案1】:

ThreadPool,unchecked 可能是个坏主意。

开箱即用,线程池per cpu 中有 250 个线程。

想象一下,如果您在一次突发事件中破坏了某人的网络连接,并禁止他们从某个站点接收通知,因为他们被怀疑正在进行 DoS 攻击。

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

虽然这可以通过 ThreadPool 进行编排,但拥有 专用 线程或使用诸如 BackgroundWorker 类的一堆实例是更好的选择。

【讨论】:

  • 你提出了一些非常好的观点,谢谢。我倾向于BackgroundWorker。有多少实例是“太多”?我知道这完全取决于用户的连接,但是超过一定数量(比如 25?)的任何东西都会造成同样的问题,是吗?
  • 在您的应用中有多个默认值,每种连接类型一个,让用户选择她的互联网速度。我会选择 3 个线程,每个域的下端限制为 1 个,上端限制为 10 个。 25 似乎偏高。但真正找出答案的唯一方法是反复试验。
【解决方案2】:

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

不要忘记,Microsoft 会花费一些时间来尽可能提高 ThreadPool 的效率。你认为你可以写一些更有效率的东西吗?我知道我做不到。

【讨论】:

  • 不,我认为我做不到 :) 我担心的是 Windows 的最大连接数限制为 10 个(我认为是每个应用程序)。其他 30 个线程是否在等待空闲连接,而实际上只有 10 个线程在做实际工作?这就是我想知道的。
【解决方案3】:

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

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

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

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

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

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

【讨论】:

    【解决方案4】:

    几点,并结合信息形成其他一些答案:

    • 您的 ThreadProc 不包含异常处理。您应该添加它,否则 1 个 I/O 错误将停止您的进程。
    • Sam Saffron 非常正确,您应该限制线程数。您可以使用 (ThreadSafe) 队列将您的提要推送到 (WorkItems) 中,并让 1+ 个线程从队列中循环读取。
    • BackgrounWorker 可能是个好主意,它可以为您提供所需的异常处理和同步。
    • BackgrounWorker 使用 ThreadPool,这很好

    【讨论】:

      【解决方案5】:

      您可能想看看"BackgroundWorker" 类。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-06-16
        • 1970-01-01
        • 1970-01-01
        • 2017-03-12
        • 1970-01-01
        • 1970-01-01
        • 2016-11-05
        • 2015-04-07
        相关资源
        最近更新 更多