将许多文件移入和移出不同目录的最佳 .NET 方法?
我创建了一个程序,可以将文件移入和移出各个目录。我遇到的一个问题是当您尝试移动文件而其他程序仍在使用它时。然后你会得到一个错误。留着它没有办法,所以我只能想到必须一遍又一遍地尝试移动它。但这会减慢整个程序的速度,因此我创建一个新线程并让它处理问题文件并继续处理下一个。更大的问题是,当您有太多这些问题文件,并且程序现在有太多线程尝试移动这些文件时,它就会因某些 kernel.dll 错误而崩溃。下面是我用来移动文件的代码示例:
Public Sub MoveIt()
Try
File.Move(_FileName, _CopyToFileName)
Catch ex As Exception
Threading.Thread.Sleep(5000)
MoveIt()
End Try
End Sub
如您所见,我尝试移动文件,如果出错,我会等待并一遍又一遍地移动它。我也尝试过使用 FileInfo
,但这比仅使用 File
对象崩溃得更快。
那么有没有人找到一种万无一失的方法来移动文件而不会出错呢?
注意:需要大量文件才会导致崩溃。周末还好,但到周一结束时,一切就结束了。
更新
我很欣赏到目前为止的所有想法。也许我应该提供更多有关我正在做的事情的信息。
这一切都是在 Windows 服务中完成的。必须移动文件。我不可能留下任何人。这就是为什么我必须一遍又一遍地尝试移动这些文件。这些文件用于将数据导入到各种数据库中。另外,没有用户可以判断文件是否无法移动。此外,该程序每天处理数千个文件。
话虽如此。如何拥有一个高效的程序,可以在没有任何用户交互的情况下移动文件,并保证所有文件都被移动?创建这些文件的程序最终会放弃对它们的控制。它们由 FTP、Biztalk 和其他各种服务创建。
I've created a program that moves files to and from various directories. An issue I've come across is when you're trying to move a file and some other program is still using it. And you get an error. Leaving it there isn't an option, so I can only think of having to keep trying to move it over and over again. This though slows the entire program down, so I create a new thread and let it deal with the problem file and move on to the next. The bigger problem is when you have too many of these problem files and the program now has so many threads trying to move these files, that it just crashes with some kernel.dll error. Here's a sample of the code I use to move the files:
Public Sub MoveIt()
Try
File.Move(_FileName, _CopyToFileName)
Catch ex As Exception
Threading.Thread.Sleep(5000)
MoveIt()
End Try
End Sub
As you can see I try to move the file, and if it errors, I wait and move it again over and over again. I've tried using FileInfo
as well, but that crashes WAY sooner than just using the File
object.
So has anyone found a fool proof way of moving files without it ever erroring?
Note: It takes a lot of files to make it crash. It'll be fine on the weekend, but by the end of the day on monday, it's done.
UPDATE
I appreciate all the ideas so far. Perhaps I should give more information about what I'm doing.
This is all done in a Windows service. The files MUST be moved. There's no way I can leave any behind. Which is why I must try OVER and OVER again to move these files. The files are used to import data into various databases. Plus there is NO user to tell if the file cannot be moved. Also, this program processes THOUSANDS of files a day.
So with that said. How can I have an efficient program that can move files without any user interaction, and guarantee that all the files get moved? The programs that create these files eventually give up their hold on them. They get created by FTP, Biztalk and other various services.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
为什么不创建一个需要移动的文件队列,当您的服务发现要移动的新文件时(假设它执行某种连续扫描,您没有提到这部分),您可以将它们添加到队列中,这可以然后由第二个线程处理,该线程不断地从队列头部取出文件并尝试移动它。如果由于文件被锁定而导致移动失败,只需将其重新插入队列末尾并继续即可。这样你就只需要担心 2 个线程,如果文件最终被释放,那么一切都会好起来的。
我会考虑用每个文件在队列中花费的时间和移动尝试的次数来标记每个文件,一旦达到某个阈值(例如无法移动 3 小时/20 次尝试),然后向合适的人。
Why not create a queue of files that need to be moved, as your service discovers new files to move (assuming it does some kind of continuous scanning, you didn't mention this part), you can add them to the queue, which could then be processed by a second thread which continually takes the file from the head of the queue and attempts to move it. If the move fails because the file is locked, just re-insert it to the back of the queue and continue. That way you only have 2 threads to worry about, and if the files do get released eventually then all should be well.
I would consider tagging each file with the time it has spent in the queue and number of move attempts made, and once it reaches a certain threshold (e.g. unable to move for 3 hours / 20 attempts) then send an e-mail alert to an appropriate person.
Windows 不是 Unix,因此您不能指望能够移动打开的文件。如果它正在使用中,则无法移动它。除非打开文件的进程明确禁止这样做,否则您可以复制文件,即使它们正在使用中。我不确定对于已打开写入的文件是否有任何数据保证。我最好的猜测是,您必须知道自己所做的事情是否安全。例如,读取正在附加的日志文件是安全的,但读取打开以进行随机访问的文件则不安全。
我的建议是列出您无法移动的文件,可以选择复制那些可以移动的文件,并为用户提供在某个时候手动重试失败文件的选项。
Windows is not Unix, so you can't expect to be able to move an open file. If it's in use then moving it is not possible. You can, unless the process opening the file has expressly prohibited this, copy files even though they are in use. I'm not sure if there are any data guarantees to be made for files that have been opened for writing. My best guess is that you have to know whether what you are doing is safe. For instance, reading an log file that is being appended to would be safe, but reading a file that is open for random access would not.
My recommendation would be to make a list of files that you were unable to move, optionally copying those that you can and giving the user an option to manually retry the failed files at some point.
我可以建议一些改进:
There are some number of improvements i can suggest:
您可以尝试使用 ThreadPool.QueueUserWorkItem 将工作排队,这可能会阻止您的线程失控
you could try using ThreadPool.QueueUserWorkItem to queue up the work, that may keep your threads from getting out of control
就像@Morten 所说,您应该首先检查移动操作失败的原因,并尝试检测/通知用户,并做一些更聪明的事情,然后重试。
关于您的代码:
您不应该一开始就进行递归调用。如果文件长时间保持锁定状态,您的堆栈会变得越来越大,可能会出现 StackOverflowException,具体取决于它运行的时间和超时的实际值)。重试该文件应该在循环内完成,如果“N”次尝试后没有成功,则会出错。请
注意,这样做时,需要 5 * 10 = 50 秒(!)才能发现文件确实无法移动,并且您仍然需要咨询用户。我认为以这种方式重试代码没有多大意义。
Like @Morten said, you should check out why the move operation fails in the first place, and attempt to detect/notify the user, and do something smarter then just retry it.
Regarding your code:
You shouldn't make the call recursive to start with. If a file stay locked for a long time your stack gets bigger and bigger, with possible StackOverflowException, depending on how long it runs and the actual value of the timeout). Retrying the file should be done inside a loop instead, that errors out when it doesn't succeed after 'N' tries. Something like
Please note that when doing it this way, it takes 5 * 10 = 50 seconds(!) to find out that a file really cannot be moved, and you still have to consult the user. I don't think there's much point in retrying the code this way.