我有一个过程需要间歇性地将一些文件从一个目录移动到另一个目录。在移动文件之前,我需要确保用户当前没有向目录添加文件 - 如果有,我希望等待 N 秒内没有新文件添加到目录,然后再继续该过程(N 的值在这里并不重要)。
我最初的想法是使用FileSystemWatcher
捕获文件创建事件,使用.Throttle()
Rx 中的方法获取指定时间段内生成的最后一个事件,并使用该结果禁用无限延迟循环。问题是,如果用户从未对目录进行任何更改,则此方法将永远等待。不知何故,我需要它等待至少N 秒,如果该时间段内没有变化,则继续。如果该时间段内有变化,则等待自上次更改以来过去 N 秒。
以下是我目前所掌握的信息:
Console.WriteLine("start");
// wait to ensure no changes are being made to the directory
await Task.Run(async () =>
{
var watcher = new FileSystemWatcher("D:\\Users\\mwcjk1\\source\\repos\\RandomTestAppNet8\\RandomTestAppNet8\\");
watcher.EnableRaisingEvents = true;
watcher.Created += (o, a) => { Console.WriteLine("file changed!"); };
var subject = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
ev => watcher.Created += ev,
ev => watcher.Created -= ev
);
bool done = false;
// wait for 7 seconds after the last file creation
var disposable = subject.Throttle(TimeSpan.FromSeconds(7)).Subscribe(events => { done = true; });
Console.WriteLine("Set up observer");
while (!done)
{
await Task.Delay(1000);
//Console.WriteLine("Waited 1 sec");
}
disposable.Dispose();
});
// Continue with the rest of the method execution
Console.WriteLine("done!");
我也不喜欢Task.Delay()
循环——感觉应该有一种更优雅的方式来做到这一点(.Throttle()
看起来很接近独立工作!),但我还没能弄清楚。
非常感谢您的任何建议,谢谢!
首先,对于这一点:
TaskCompletionSource是事件/反应世界与世界之间的适配器。基本上,它公开了一个可以通过调用完成源上的方法来完成、出错或取消的任务。
Task
Task
Task.Delay
您可以用以下代码重写您的代码:其次,你还提到
这是因为您正在等待事件触发,但直到目录发生更改时事件才会触发。
我建议你检查一下目录最近是否有任何变化,如果有的话,请稍等片刻,然后重试。例如:
现在的问题是
HasAnyRecentChanges
实现会是什么样子。我建议从最简单的开始——只需检查目录中文件的最后修改时间。对于大量文件来说,这可能会很慢,因此使用
FileSystemWatcher
并保留字段中的最后修改时间可能是一种优化,特别是如果您非常频繁地运行它或对于较小的 N。但是,在启动时仍需要加载所有文件的最后修改时间来初始化字段,因此在任何一种情况下都需要实现。抱歉,没有 RX,我不认为我能用它使它变得更干净。
一种可能的方法是使用一个计时器,每次用户做出更改时都会重置。例如:
这是相当低级的代码,使用阻塞代码而不是任何异步代码,但它仍然相当简单。如果您需要异步代码,您可能可以用 TaskCompletion 源替换 resetEvent。进行 FileSystemWatcher 调用
OnFileSystemEvent
留作练习。您可能希望将其设为一个长寿命对象,这样即使您没有复制任何文件,它也可以监视更改。或者,在启动时检查任何文件的修改日期,如果在延迟时间内修改了任何文件,则将重置事件初始化为 false,并使用剩余到期时间初始化计时器。
您可能需要查找术语“debouncer”。这是一个我尚未测试过的简单实现:
https://gist.github.com/lcnvdl/43bfdcb781d799df6b7e8e66fe3792db
但在我看来还不错。
每次目录发生变化时,你只需调用 Debounce() 并传递将复制文件的方法。
尝试一下,输入 - 是文件系统观察事件的来源:
无论如何,这是我在参考了其他一些答案/评论后做出的实现: