使用多线程 for 循环

Posted

技术标签:

【中文标题】使用多线程 for 循环【英文标题】:Using multithreading for loop 【发布时间】:2011-02-13 21:04:24 【问题描述】:

我是线程新手,想做一些类似这个问题的事情:

Speed up loop using multithreading in C# (Question)

但是,我不确定该解决方案是否最适合我,因为我希望它们继续运行并且永远不会完成。 (对于那个问题,我也在使用 .net 3.5 而不是 2.0。)

我想做这样的事情:

foreach (Agent agent in AgentList)

    // I want to start a new thread for each of these
    agent.DoProcessLoop();


---

public void DoProcessLoop()

    while (true)
    
        // do the processing

        // this is things like check folder for new files, update database
        // if new files found
    

ThreadPool 会是最好的解决方案,还是有更适合的解决方案?

更新:感谢所有出色的答案!我想我会更详细地解释用例。许多代理可以将文件上传到一个文件夹。每个代理都有自己的文件夹,他们可以将资产上传到(csv 文件、图像、pdf)。我们的服务(它应该是在他们上传资产的服务器上运行的 Windows 服务,请放心,我很快就会回来提出有关 Windows 服务的问题 :))如果有任何新资产,将继续检查每个代理的文件夹,如果有,将更新数据库并为其中一些创建静态 html 页面。由于他们可能需要一段时间才能上传所有内容,并且我们希望他们能够立即看到他们上传的更改,我们认为每个代理一个线程是一个好主意,因为没有代理需要等待其他人完成(我们有多个处理器,所以想使用它们的全部容量)。希望这能解释清楚!

谢谢,

安妮

【问题讨论】:

有点切题,但您可能希望尝试使线程更多地由事件驱动,而不是在紧密循环中运行。像这样在一个紧密的循环中运行很可能最终会破坏您的处理器并浪费资源,并且会使有实际工作要做的线程停留更长的时间。 更多线程不一定是答案,可能会成为问题。假设您有 20 个代理并为每个代理生成一个线程。文件 i/o 总是会成为瓶颈,因此您有 20 个线程都在不断地轮询文件系统以进行更改——这将对性能产生负面影响。这些线程中的每一个也将使用 CPU 时间进行轮询,因此在一个四核机器上,每个核心都会受到其中五个线程(假设有 20 个代理)的影响,所以一个正在做实际工作的线程(处理新的文件)将被换出以允许执行轮询。 【参考方案1】:

鉴于您描述的特定用途(查看文件),我建议您使用FileSystemWatcher 来确定何时有新文件,然后使用线程池启动线程来处理文件,直到没有更多文件处理——此时线程退出。

这应该会减少 i/o(因为您不会经常轮询磁盘),减少 CPU 使用率(因为轮询磁盘的多个线程的持续循环会使用周期),并减少您正在运行的线程数任何时候(假设没有对文件系统进行持续修改)。

您可能希望仅在主线程上打开和读取文件并将数据传递给工作线程(如果可能),以将 i/o 限制为单个线程。

【讨论】:

正要自己发这个。 这是正确的方法。您的 FileSystemWatcher 事件处理程序应将新文件发布到队列,该队列由单独的主管线程监视,该线程产生单独的工作线程以摄取文件。这使您可以控制工作线程的数量并最大限度地减少 FileSystemWatcher 缓冲区溢出的可能性。每个工作线程都应该不断测试,直到它获得对其文件的独占访问权,这样您才能在文件的编写器完成之前开始阅读。【参考方案2】:

我相信 Parallels 扩展使这成为可能:

Parallel.Foreach

http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallel.foreach.aspx http://blogs.msdn.com/pfxteam/

【讨论】:

【参考方案3】:

ThreadPool 的一个问题是,如果池恰好小于您希望拥有的代理数量,那么您稍后尝试启动的代理可能永远不会执行。有些任务可能永远不会开始执行,并且您可能会饿死您的应用程序域中使用线程池的其他所有内容。你最好不要走那条路。

【讨论】:

@tehMick 有趣的一点!您对该怎么做有什么建议吗?我们想做线程的原因是我们不希望任何上传内容的人不得不等待太久,并且由于我们有多个处理器,所以我们会使用它们的全部容量。 如果您正在监视文件系统,我肯定会在单个线程上执行此操作,因为多个线程只会争夺文件访问权限。然后,您找到的每个文件都可以作为工作项排队等待线程池拾取。但是还有许多其他有效的方法可以解决它。 虽然,想多了,我可能会误解这里的用例,我对上下文并不完全清楚。【参考方案4】:

您绝对想为此目的使用 ThreadPool。 ThreadPool 线程不适合用于长时间运行的任务(“无限”算作长时间运行),因为这显然会占用本应共享的资源。

对于您的应用程序,最好创建一个线程(不是来自 ThreadPool)并在该线程中执行您的 while 循环,在其中您遍历您的 Agents 集合并对每一个进行处理。在while 循环中,您还应该使用Thread.Sleep 调用,这样您就不会最大限度地使用处理器(有更好的定期执行代码的方法,但 Thread.Sleep 将适用于您的目的)。

最后,您需要为程序终止时退出 while 循环添加一些方法。

更新:最后,多线程不会自动加速运行缓慢的代码。九个女人不能在一个月内生孩子。

【讨论】:

但是八月妈妈每个月可以生一个孩子... :-P 虽然线程通常确实会加快处理时间,尤其是在现代台式机和服务器机器的这些多核处理器中。长久以来,没有每个线程需要的单一资源,即使在单核机器上它们也会执行得更快。此外,ThreadPool 是要被使用的!我真的不认为他们的意思是(真的)除了psudocode的目的。 @Nate:OQ 说“我希望他们继续奔跑,永远不会结束”,我认为他们的意思是字面意思 while (true)。当然,ThreadPool 是要被使用的——只是不是这样。【参考方案5】:

当您期望线程相当有规律地进入和退出存在时,线程池很有用,而不是预定义的一组线程数。

【讨论】:

@Tejs 是的,我明白你的意思。有什么建议可以代替吗? (请参阅我之前的评论,了解我们为什么这样做) 我永远不会实现 while(true) 循环——除非你想抛出异常,或者只是杀死线程,否则这是无法终止的。如果您需要并发,请考虑执行以下操作:
 Thread x = new Thread(new ThreadStart(Foo)); x.开始(); // 如果以后某些条件发生变化,而你想杀死它,调用 x.Abort() public void Foo()  // Do Stuff  
【参考方案6】:

嗯.. 正如 Ragoczy 指出的那样,最好使用 FileSystemWatcher 来监视文件。但是,由于您有额外的操作,您可能会考虑多线程。

但请注意,无论您拥有多少个处理器,其容量都是有限度的。您可能不想创建与并发用户数量一样多的线程,原因很简单,您的代理数量可能会增加。

【讨论】:

【参考方案7】:

在您升级到 .NET 4 之前,ThreadPool 可能是您的最佳选择。您可能还想使用 Semaphore 和 AutoResetEvent 来控制并发线程的数量。如果您正在谈论长时间运行的工作,那么启动和管理自己的线程的开销很低,并且解决方案更加优雅。这将允许您使用 WorkerThread.Join() 以便在恢复执行之前确保所有工作线程都已完成。

【讨论】:

以上是关于使用多线程 for 循环的主要内容,如果未能解决你的问题,请参考以下文章

为啥我使用多处理/多线程的函数在 for 循环中使用时如此缓慢,但在循环之外却没有?

python循环怎么用多线程去运行

多线程使用for循环遍历同一个Set,是否线程安全?

java多线程死循环

在 Python3 中使用 for 循环进行多线程/多处理

java for循环中创建线程池