这是使用线程池的正确情况吗?
设置如下:我正在尝试制作一个相对简单的 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 上,我认为连接数如此之多,某个地方会出现瓶颈。我说得对吗?
我可以做什么来确保线程/连接的最大效率?
所有这些问题也让我想知道这是否适合使用线程池。 MSDN 和其他消息来源称它应该用于“短期”任务。考虑到我的连接速度相对较快,1-2 秒的“短暂”时间是否足够?如果用户使用 56K 拨号并且一个请求可能需要 5-12 秒甚至更长时间,该怎么办?那么线程池也是一个有效的解决方案吗?
Here's the setup: I'm trying to make a relatively simple Winforms app, a feed reader using the FeedDotNet library. The question I have is about using the threadpool. Since FeedDotNet is making synchronous HttpWebRequests, it is blocking the GUI thread. So the best thing seemed like putting the synchronous call on a ThreadPool thread, and while it is working, invoke the controls that need updating on the form. Some rough code:
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
= main form instance
updatesPending
= volatile int in the main form
ProcessFeedResult
= method that does some operations on the Feed object. Since a threadpool thread can't return a result, is this an acceptable way of processing the result via the main thread?
The main thing I'm worried about is how this scales. I've tried ~250 requests at once. The max number of threads I've seen was around 53 and once all threads were completed, back to 21. I recall in one exceptional instance of me playing around with the code, I had seen it rise as high as 120. This isn't normal, is it? Also, being on Windows XP, I reckon that with such high number of connections, there would be a bottleneck somewhere. Am I right?
What can I do to ensure maximum efficiency of threads/connections?
Having all these questions also made me wonder whether this is the right case for a Threadpool use. MSDN and other sources say it should be used for "short-lived" tasks. Is 1-2 seconds "short-lived" enough, considering I'm on a relatively fast connection? What if the user is on a 56K dial-up and one request could take from 5-12 seconds and ever more. Would the threadpool be an efficient solution then too?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
ThreadPool,未检查可能是一个坏主意。
线程池中开箱即有 250 个线程 每个 CPU。
想象一下,如果您在一次爆发中压垮了某人的网络连接,并禁止他们从某个网站接收通知,因为他们被怀疑正在运行 DoS 攻击。
相反,当从网上下载东西时,你应该建立大量的控制。用户应该能够决定他们发出多少个并发请求(以及每个域有多少个并发请求),理想情况下您还希望提供对带宽量的控制。
尽管这可以通过 ThreadPool 进行编排,但拥有专用线程或使用诸如一堆 BackgroundWorker 类实例之类的东西是更好的选择。
The ThreadPool, unchecked is probably a bad idea.
Out of the box you get 250 threads in the threadpool per cpu.
Imagine if in a single burst you flatten out someones net connection and get them banned from getting notifications from a site cause they are suspected to be running a DoS attack.
Instead, when downloading stuff from the net you should build in tons of control. The user should be able to decide how many concurrent requests they make (and how many concurrent requests per domain), ideally you also want to offer controls for the amount of bandwidth.
Though this could be orchestrated with the ThreadPool, having dedicated threads or using something like a bunch of instances of the BackgroundWorker class is a better option.
我对ThreadPool的理解是它就是为了这种情况而设计的。我认为短暂的定义是这样的时间顺序——甚至可能长达几分钟。 “长寿命”线程是指在应用程序的整个生命周期内都处于活动状态的线程。
不要忘记,微软会花费一些时间来尽可能提高线程池的效率。你认为你可以写一些更有效的东西吗?我知道我不能。
My understanding of the ThreadPool is that it is designed for this type of situation. I think the definition of short-lived is of this order of time - perhaps even up to minutes. A "long-lived" thread would be one that was alive for the lifetime of the application.
Don't forget Microsoft would have spent some getting the efficiency of the ThreadPool as high as it could. Do you think that you could write something that was more efficient? I know I couldn't.
.NET 线程池专为执行短期运行的任务而设计,对于这些任务,创建新线程的开销会抵消创建新线程的好处。它不适用于长时间阻塞或执行时间较长的任务。
这个想法是让一个任务跳到一个线程上,快速运行,完成并跳出。
BackgroundWorker
类提供了一种在线程池线程上执行任务的简单方法,并提供了任务报告进度和处理取消请求的机制。在关于BackgroundWorker组件的这篇MSDN文章中,明确给出了文件下载作为适当使用此类的示例。这应该会鼓励您使用此类来执行您需要的工作。
如果您担心过度使用线程池,您可以放心,运行时确实会根据需求管理可用线程的数量。任务在线程池中排队等待执行。当线程可以执行工作时,任务就会加载到该线程上。监视进程会定期检查线程池的状态。如果有任务等待执行,它可以创建更多线程。如果有多个空闲线程,可以关闭一些线程以释放资源。
在最糟糕的情况下,所有线程都很忙并且您有工作排队,运行时将添加线程来处理额外的工作负载。应用程序将运行得更慢,因为它必须等待更多线程可用,但它将继续运行。
The .NET thread pool is designed specifically for executing short-running tasks for which the overhead of creating a new thread would negate the benefits of creating a new thread. It is not designed for tasks which block for prolonged periods or have a long execution time.
The idea is to for a task to hop onto a thread, run quickly, complete and hop off.
The
BackgroundWorker
class provides an easy way to execute tasks on a thread pool thread, and provides mechanisms for the task to report progress and handle cancel requests.In this MSDN article on the BackgroundWorker Component, file downloads are explicitly given as examples of the appropriate use of this class. That should hopefully encourage you to use this class to perform the work you need.
If you're worried about overusing the thread pool, you can be assured the runtime does manage the number of available threads based on demand. Tasks are queued on the thread pool for execution. When a thread becomes available to do work, the task is loaded onto the thread. At regular intervals, a monitoring process checks the state of the thread pool. If there are tasks waiting to be executed, it can create more threads. If there are several idle threads, it can shut down some to release resources.
In a worse-case scenario, where all threads are busy and you have work queued up, the runtime will be adding threads to deal with the extra workload. The application will be running more slowly as it has to wait for more threads to be made available, but it will continue to run.
有几点,并结合信息形成一些其他答案:
A few points, and to combine info form a few other answers:
您可能需要查看"BackgroundWorker" 类。
You may want to take a look to the "BackgroundWorker" class.