在进入c#时同时处理文件

时间:2011-07-12 00:55:28

标签: c# multithreading

我的应用程序非常适合处理位于我服务器上的目录中的文件。这个过程是:

1) check for files in a directory
2) queue a user work item to handle each file in the background
3) wait until all workers have completed
4) goto 1

这很好用,我从不担心同一文件被处理两次或多个线程被生成同一个文件。但是,如果有一个文件需要很长时间才能处理,那么步骤#3会挂起该文件并保留所有其他处理。

所以我的问题是,为我需要处理的每个文件生成一个线程的正确范例是什么,而如果一个文件需要太长时间则不阻塞?我考虑过FileSystemWatcher,但文件可能无法立即读取,这就是为什么我不断查看所有文件并为每个文件生成一个进程(如果文件被锁定将立即退出)。

我应该删除第3步并维护我已经处理过的文件列表吗?这似乎很混乱,随着时间的推移,列表会变得非常大,所以我怀疑这是一个更优雅的解决方案。

4 个答案:

答案 0 :(得分:6)

我建议您维护一份当前正在处理的文件列表。线程完成后,让线程从此列表中删除。查找新文件时,请排除当前运行列表中的文件。

答案 1 :(得分:3)

在启动线程之前将文件移动到处理目录。然后你可以发射并忘记线程,任何管理员都可以一目了然地看到发生了什么。

答案 2 :(得分:3)

每个要处理的项目产生一个线程几乎从来都不是好方法。在您的情况下,当文件数量超过几百个单个每个文件的线程将使应用程序性能非常糟糕,并且32位进程将开始耗尽地址空间。

Dark Falcon的列表解决方案非常简单,与您的算法相匹配。我实际上会使用队列(类似于ConcurrentQueue - http://msdn.microsoft.com/en-us/library/dd267265.aspx)将项目放在一边处理(即基于文件观察者的定期扫描),并选择要由另一侧的一个或多个线程处理的项目。您通常需要较少数量的线程(即,CPU密集型负载的CPU数量为1-2x)。

还可以考虑使用任务并行库(如Parallel.ForEach - http://msdn.microsoft.com/en-us/library/dd989744.aspx)来处理多个线程。

要最小化要处理的文件数量,我会保留已经处理的项目的持久性(即磁盘文件)列表 - 文件路径+上次修改日期(除非您可以从其他来源获取此信息)。

答案 3 :(得分:1)

我的两个主要问题是:

  1. 文件的大小是多少?
  2. 文件出现的频率是多少?
  3. 根据你的答案,我可能会使用以下生产者 - 消费者算法:

    1. 使用文件系统观察程序查看您正在监控的目录中是否有活动
    2. 当活动发生时,“轻轻地”开始轮询;这是测试每个文件是否可用以查看它是否未锁定(即,尝试使用通过try..catch测试的简单IsLocked扩展方法打开w / write权限);如果一个或多个文件不是空闲的,请将计时器设置为在一段时间内关闭(如果期望更少的文件更少,则更长,如果更小和/或更频繁则更短)再次测试文件
    3. 只要您看到文件是空闲的,就处理它(即将其移动到另一个文件夹,将项目放入并发队列,让您的消费者线程处理队列,归档文件/结果)。
    4. 像Alexei提到的某种持久性机制(即磁盘/数据库)能够在系统出现故障时从中断处恢复处理。
    5. 我觉得这是非阻塞,低CPU使用行为的良好组合。但要衡量你的前后结果。我建议使用ThreadPool并尝试阻止线程阻塞(即,尝试通过执行类似Thread.Sleep之类的操作来确保线程重用)

      注意:

      1. 将处理文件的线程数基于机器上可用的CPU数和内核数;也考虑服务器负载
      2. FileSystemWatcher可能很挑剔;确保它是从您正在监控的同一台计算机上运行(即,不是在观看远程服务器),否则您将需要不时重新初始化连接。
      3. 我肯定不会为每个文件生成不同的进程;多线程应足够;重用线程是最好的。产卵过程是一种非常昂贵的操作,并且产卵线程是一种昂贵的操作。阿列克谢在任务并行库中有一些很好的信息;它使用ThreadPool。