【问题标题】:Can I limit the I/O of my C# app我可以限制我的 C# 应用程序的 I/O
【发布时间】:2011-03-25 09:37:36
【问题描述】:

我构建了一个应用程序,它对数千个文件执行工作,然后将这些文件的修改副本写入磁盘。我正在使用 ThreadPool 但它产生了这么多线程,PC 变得无响应总共 260 个),所以我将最大值从默认的 250 更改为 50,这解决了这个问题(应用程序总共只产生大约 60 个线程),但是既然文件准备得如此之快,它会将 UI 捆绑到电脑无响应的地步。

有没有办法限制 I/O 的数量 - 我的意思是,我喜欢使用 50 个线程对文件执行工作,但不是在处理文件时同时写入 50 个线程。如果可以避免的话,我宁愿不重新设计文件部分的编写——我希望我可以限制来自该池的线程可以消耗的 I/O(同时)数量。

【问题讨论】:

  • 您说“文件准备得如此之快,它占用了 UI”。每次文件准备好时都会更新 UI 吗?如果是这样,这可能是这里真正的问题吗??

标签: c# multithreading io


【解决方案1】:

使用信号量来限制数量。想要同时写入磁盘的线程数。

http://msdn.microsoft.com/en-us/library/system.threading.semaphore.aspx

限制线程数 访问资源或资源池 同时。

【讨论】:

    【解决方案2】:

    你真的不需要这么多线程。磁盘只能支持其最大的读写吞吐量,如果单个线程专用于 IO 即读取或写入,则可以轻松地将其最大化。您也不能同时读取和写入硬盘(尽管这对于操作系统缓存层等很复杂),因此让并发线程读取和写入可能会适得其反。对于非 IO 任务,拥有比处理器/内核更多的线程也没什么好处,因为任何额外的线程都会花费大量时间等待内核可用,例如如果您有 50 个线程和 4 个内核,那么在任何给定时间至少有 46 个线程将处于空闲状态。浪费的线程既会导致内存消耗,也会导致性能开销,因为它们都将在某个时间在内核上争取破解,而操作系统必须仲裁这场斗争。

    更直接的方法是有一个线程,其工作是读取文件,然后将数据添加到阻塞队列(例如,参见ConcurrentQueue),同时有许多工作线程正在等待队列中的文件数据(例如,线程数等于处理器\内核数)。这些工作线程将在添加项目时穿过队列,并在队列为空时阻塞。当工作线程完成一项工作时,它可以将其添加到另一个由读取线程或专用写入线程监视的阻塞队列。它的工作是把文件写出来。

    此模式旨在在更小的一组协作线程中平衡 IO 和 CPU,其中 IO 线程的数量仅限于硬盘驱动器的物理能力,以及合理的 CPU 工作线程数量对于您拥有的处理器\内核数。从本质上讲,它将 IO 和 CPU 工作分开,从而使事情的行为更具可预测性。

    除此之外,如果 IO 确实是问题所在(而不是大量线程相互争斗),那么您可以在文件读取和写入线程中放置一些暂停(例如 Thread.Sleep)以限制多少线程他们做的工作。

    更新

    也许有必要首先解释一下为什么会生成这么多线程。这是线程池使用的退化案例,主要围绕队列中具有 IO 组件的工作项。

    线程池从其队列中执行工作项并监控执行工作项所用的时间。如果当前正在执行的工作项需要很长时间才能完成(我认为内存需要半秒),那么它将开始向池中添加更多线程,因为它认为这将使队列处理得更快\更公平。但是,如果额外的并发工作项也在对共享磁盘执行工作 IO,那么磁盘的性能实际上会降低,这意味着工作项将需要更长的时间来执行。因为工作项需要更长的时间来执行,所以线程池会添加更多线程。这是退化的情况,随着添加更多线程,性能会变得越来越差。

    必须谨慎使用建议的信号量,因为信号量可能会导致线程池线程阻塞,线程池会看到工作项需要很长时间才能执行,并且它仍会开始添加更多线程。

    【讨论】:

    • ThreadPool 会根据每个文件的内容执行复杂的计算,并使用 ThreadPool 加快这部分过程的速度:-)
    • @schmoopy 我不确定我是否理解您的评论。我熟悉线程池和文件处理,因此我回答了您的问题。详细我可能会补充...
    猜你喜欢
    • 1970-01-01
    • 2015-11-15
    • 2015-07-04
    • 1970-01-01
    • 1970-01-01
    • 2011-05-14
    • 1970-01-01
    相关资源
    最近更新 更多