Net 5 中与 FileSystemWatcher 的异步使用
我正在尝试在 Net 5 中创建一个应用程序来监视文件夹,并且任何时候将文件放入文件夹中时,它都应该运行一组特定的任务(从文件中获取一些信息,将它们复制到新位置等) )。 我以为我可以实现 Net 的 FileSystemWatcher 和并发集合(来自此处的 Collections.Concurrent 命名空间,但我遇到了无法异步运行它的问题。
我像这样初始化观察器(遵循 MS 的文档):
public BlockingCollection<string> fileNames = new BlockingCollection<string>();
public void InitiateWatcher()
{
using FileSystemWatcher watcher = new FileSystemWatcher(@"C:\temp"); //test dir
watcher.NotifyFilter = NotifyFilters.Attributes
| NotifyFilters.CreationTime
| NotifyFilters.DirectoryName
| NotifyFilters.FileName
watcher.Created += OnCreated;
watcher.Filter = "*.*";
watcher.IncludeSubdirectories = true;
watcher.EnableRaisingEvents = true;
Console.WriteLine("Press e to exit.");
Console.ReadLine();
}
private void OnCreated(object sender, FileSystemEventArgs e)
{
string value = $"Created: {e.FullPath}";
// this of course creates problems, since it is a async task running in sync mode.
await PipelineStages.ReadFilenamesAsync(_sourcePath, fileNames);
// do other work with the result from ReadFilenameAsync
Console.WriteLine(value);
}
我的 PipelineStages 类,它对文件进行大部分实际工作,如下所示:
public static class PipelineStages
{
public static Task ReadFilenamesAsync(string path, BlockingCollection<string> output)
{
return Task.Factory.StartNew(() =>
{
foreach (string fileName in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
{
output.Add(fileName);
}
output.CompleteAdding();
}, TaskCreationOptions.LongRunning);
}
}
如果我将 OnCreated 方法设为异步,它会抛出返回类型的错误这对我来说有点道理,尽管我不知道前进的方向,但
我看到两个错误: 一个错误是,当代码到达 output.add(fileName)
行时,它会抛出 System.InvalidOperationException:“集合已被标记为关于添加的完成。”
另一个是,我注意到在 onCreated 方法中,文件名被写入控制台,这些文件位于非常不同的文件夹中,看起来是随机的。
所以,基本上,我有两个问题:
- 这是正确的方法吗? (如果没有,还有什么其他选择)
- 如果这是正确的方法,我该如何解决这些问题?
I'm trying to create an application in Net 5 that watches a folder and any time files are being dropped in the folder, it should run a certain set of tasks (getting some info from the files, copy them to new location, among others).
I thought I could implement both Net's FileSystemWatcher
and concurrent collections (from the Collections.Concurrent
namespace here, but I run into the problem that I cannot run it with being async.
I initialize the watcher like this (following the docs of MS):
public BlockingCollection<string> fileNames = new BlockingCollection<string>();
public void InitiateWatcher()
{
using FileSystemWatcher watcher = new FileSystemWatcher(@"C:\temp"); //test dir
watcher.NotifyFilter = NotifyFilters.Attributes
| NotifyFilters.CreationTime
| NotifyFilters.DirectoryName
| NotifyFilters.FileName
watcher.Created += OnCreated;
watcher.Filter = "*.*";
watcher.IncludeSubdirectories = true;
watcher.EnableRaisingEvents = true;
Console.WriteLine("Press e to exit.");
Console.ReadLine();
}
private void OnCreated(object sender, FileSystemEventArgs e)
{
string value = quot;Created: {e.FullPath}";
// this of course creates problems, since it is a async task running in sync mode.
await PipelineStages.ReadFilenamesAsync(_sourcePath, fileNames);
// do other work with the result from ReadFilenameAsync
Console.WriteLine(value);
}
My PipelineStages class, which does most of the real work with the files, looks like this:
public static class PipelineStages
{
public static Task ReadFilenamesAsync(string path, BlockingCollection<string> output)
{
return Task.Factory.StartNew(() =>
{
foreach (string fileName in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
{
output.Add(fileName);
}
output.CompleteAdding();
}, TaskCreationOptions.LongRunning);
}
}
If I turn the OnCreated method async it throws error that the return type is not valid. That kinda makes sense to me, although I don't know the way forward on this.
I see two errors:
One error is when the code hits the output.add(fileName)
line, it throws an System.InvalidOperationException: 'The collection has been marked as complete with regards to additions.'
The other one is that I notice in the onCreated method that filenames get written to the console that are in very different folders, seemingly randomly.
So, basically, I have two questions:
- Is this the right approach? (If not, what are other alternatives)
- If this is the right approach, how can I fix these problems?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
首先,您似乎过早地调用了
output.CompleteAdding()
,因此每次执行PipelineStages.ReadFilenamesAsync
都会收到上述System.InvalidOperationException
错误。其次,虽然
async void
通常确实不鼓励,但在以下情况下是允许的:Main
方法,但只要这样的
async void
code> 事件处理程序基本上是一个即发即忘函数,您应该使其对于任何捕获的变量(其状态可以在外部作用域中无效)(已处置,CompleteAdding 等) .我编写了以下异步设计的简短独立示例来演示我想要表达的观点:
Firstly, it looks like you prematurely call
output.CompleteAdding()
, so every subsequent execution ofPipelineStages.ReadFilenamesAsync
gets the foregoingSystem.InvalidOperationException
error.Secondly, while
async void
is indeed generally discouraged, it is admissible in the following cases:Main
methodBUT as long as such an
async void
event handler is basically a fire‑and‑forget function, you should make it bulletproof with regard to any captured variables whose state can be invalidated in the outer scope (disposed, CompleteAdding, and the like).I have rigged up the following async‑contrived short self‑contained example to demonstrate the point I am trying to make: