C# 手动线程

3
有没有好的资源可以展示如何在C#中创建无限数量的线程而不使用ThreadPool?
我意识到有人可能会质疑拥有数百或数千个线程的系统的架构,因此让我解释一下任务,以防CPU / OS使这种努力无用。
我有大约2500个URL需要测试。 其中一些非常缓慢:响应时间超过10秒。 无论如何,网络延迟占每个操作的99.99%。
我想尽快测试所有2500个网址。
我连接了一个测试,测试它们的各自线程。
问题是我正在使用ThreadPool,我认为默认限制是25,所以这样不好。 我需要手动管理它们。 我错了吗?
我知道CPU / OS可能还会限制每个CPU的并发线程数,但我相信这个限制比25高得多。
关于架构,我意识到如果我连接了2,000个HTTP线程,我可能会锁定整个箱子,但这是一个在隔离中运行并且可以使用所有可用资源的管理员任务。
感谢您的见解。

你为什么认为你的代码可以比线程池更好地执行线程任务? - Juliet
线程池据我所知有25个线程的限制。我正在尝试绕过这个限制。如果有办法解决它,那么我很乐意让ThreadPool为我管理一切。 - Scott Klarenbach
从阅读本网站和其他网站的经验来看,人们倾向于认为,如果你想管理比线程池允许的更多的线程,你需要实现某种手动解决方案。 - Scott Klarenbach
Scott - 你可以将线程添加到线程池中。不过,我建议保持适度。 - Reed Copsey
4
过多的线程可能会拖慢整个系统,因此只需增加线程池的大小即可。不要有2500个线程,而是约100个左右。一旦一个线程完成一个URL,则可以移动到下一个URL。这样,慢速的URL将不会影响其他线程。 另一方面,如果您同时有2500个线程尝试访问,大多数都将超时,因为系统无法处理每个响应,因为它忙于在每个线程之间切换。 - Virat Kadaru
很遗憾的是,C# 没有像 Java 的并发包那样的东西。在 Java 中,你可以轻松地构建一个线程池,就像这样:ExecutorService myPool = Executors.newFixedThreadPool(100); - pjp
10个回答

11

你无法创建无限数量的线程。 如果你试图这样做,你将遇到许多问题。

然而,在C#中,你可以增加线程池中默认的线程数。只需使用ThreadPool.SetMaxThreads来为线程池提供更多的线程以便它工作。它很可能比任何手动创建线程的尝试做得更好(除非你对手动过程投入了大量的精力)。


正确,每个CPU的最大线程数为256。我假设最佳实践建议是不要超过这个值,因为切换开销会适得其反。然而,我提出此问题的主要原因是每个线程的网络延迟时间太长了,以至于我愿意承担大量与线程相关的开销,以免在网络排队等待。 - Scott Klarenbach
尝试使用您当前的方法,但将线程池上的最大线程数设置得更高。这可能是您最好的选择。我确定并非所有测试用例都需要很长时间-因此,那些将快速完成,并且您最终只会等待坏情况。即使您可以这样做,对于2500个URL而言,2500个线程实际上可能需要更长的时间来进行线程切换和线程创建开销。 - Reed Copsey
您的有效线程限制可能只有约2000个线程,这取决于链接器为每个线程分配的默认堆栈大小。请参见http://blogs.msdn.com/oldnewthing/archive/2005/07/29/444912.aspx。 - Ed Power

4

您还需要注意,Windows XP(可能也包括Vista/Win7)对半开放的 TCP 连接数有限制(10),如果等待响应的站点不存在,添加更多线程并不能解决这个问题。


1
该限制已经在Vista SP2和Windows 7中被移除。 - Ed Power

3

好吧,ThreadPool 中的最大线程数为 256,如果需要更多,您将不得不手动执行。 (编辑: 哎呀--那只适用于紧凑框架)

手动启动一个新线程很容易:

Thread newThread = new Thread(new ThreadStart(myWorkerMethod));
newThread.Start();

话虽如此,您可能应该重新考虑您的方法。如果您需要那么多线程,很有可能您的方法不正确。


但这不是只使用线程池中的一个线程吗?正如其他数字所述,每个 CPU 的线程数不能超过 256。 - David McEwing

2
非常感谢大家,我希望我能够接受多个答案。
我的做法是:
a.) 将MaxThreads属性设置为100。 b.) 将

标签放置在适当的位置,以确保HTML代码正确。
<system.net>
    <connectionManagement>
      <add address="*" maxconnection="100" />
    </connectionManagement>
  </system.net>

配置文件中的代码

c.) 将XP的TCP/IP限制从10提高到100。

d.) 修改ServicePointManager.DefaultConnectionLimit为100。

这些解决方案结合起来极大地提高了性能。

现在我发现亨利(Henri)的评论非常有道理。

我甚至可能不需要线程......我可以让同一个线程调用WebClient.DownloadStringAsync,这将模拟我所拥有的线程,但会简单得多。

问题又在于,我可能会遇到内部WebClient/.NET的限制,然后需要解决这些限制......


2
你可能需要考虑使用不同的线程池提供程序;来自MiscUtil(由Jon Skeet编写)的MiscUtil.Threading.CustomThreadPool提供了一个自定义线程池实现,允许您指定最大线程数,同时确保仅有一组任务/应用程序正在使用线程池。
更准确地说,虽然你可能想要启动50-1000个线程,但是在线程分配工作方面,你可能不应该重复造轮子。
此外,如果你正在使用HttpWebRequest和HttpWebResponse进行URL检查,则可能还需要修改:ServicePointManager.DefaultConnectionLimit。默认情况下,存在并发Web请求的数量限制(2或10),这会严重阻碍拥有能够运行数百个线程的机器所带来的任何可能好处。

谢谢CoderTao,ServicePointManager限制也提高了性能。 - Scott Klarenbach

2

我不确定这个答案是否已经被提出,但我认为没有必要超过2/3个线程。

一个线程处理所有请求并完成。 第二个线程等待回复,一旦收到回复,它将回复排队到回复队列中。 第三个线程出队并处理回复。

就是这么简单。

有一个问题,我不确定在.NET中是否可以异步接收HTTP响应。 我假设可以,但我不确定。

要么我完全误解了重点,要么你们想得太复杂了。


是的,在 .Net 中你可以进行异步 HTTP 操作。 - Frank Schwieterman
这很不错,亨利...我唯一关心的是WebClient对异步下载有什么限制? - Scott Klarenbach

1

虽然需要测试2500个URL表明您需要2500个线程,但您很可能不需要全部2500个。线程池将快速回收那些响应迅速的网址所使用的线程。

因此,您可能会看到几十个线程的高峰。除此之外,我怀疑更多的线程不会显著提高性能。由于线程开销,您将达到收益递减点。


1

我曾经遇到过类似的问题,但与其使用线程池,我为每个想要请求的URL创建了一个线程,并将其添加到队列中,然后弹出每个线程并启动它。我跟踪正在运行的线程数,当每个线程完成时,我就获取下一个等待线程。在我的用户界面中,我可以调整最大运行连接数,并监视排队连接数。

你所面对的问题是活动连接数量超过活动线程数量,因为这些线程在等待响应时被阻塞。线程池重用线程并节省创建和启动线程的开销,这有助于处理需要大量CPU资源的任务;25是一个合理的限制,除非你有很多核心。但是当你在等待网络连接时,线程开销是微不足道的。

你可以通过设置maxconnection值在你的app.config中设置最大限制(默认为2):http://msdn.microsoft.com/en-us/library/aa903351%28VS.71%29.aspx

您可以创建大量的线程,但只有开启的线程会被“计费”。您受系统资源的限制,但我已经成功地创建了数百个线程,而且性能没有受到严重影响。


1
你可能想要阅读关于构建能够处理超过一万个连接的软件的C10K问题,其中列出的大多数方法在Windows中都有类似物。Codeguru上有一个C#异步套接字介绍。基本上,在异步IO中,不是切换线程上下文并且每个线程测试一个套接字,而是使用事件驱动方法,它钩入操作系统套接字实现以报告可用的套接字。你还可以调整注册表中的一些Windows TCP设置,例如最大连接数。

0

你可以同时运行多个应用程序实例,并在任务管理器中提高其优先级。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接