首先是一些背景,我编写了一个名为Duplicitiy(在github.com上)的开源.NET库,它使用FileSystemWatcher
复制两个目录之间的所有文件更改。
我编写了一个FileSystemObservable
类来实现IObservable<FileSystemChange>
(使用FSWatcher来包装实际的FileSystemWatcher
)。创建,修改或删除文件或目录时,使用Reactive Extensions通过Subject<FileSystemChange>
发布更改。
然后我使用以下订阅订阅此observable。
return observable
.Buffer(() => observable.Throttle(TimeSpan.FromSeconds(2)).Timeout(TimeSpan.FromMinutes(1)))
.PrioritizeFileSystemChanges()
.SelectMany(x => x);
更改被缓冲,直到至少有2秒的时间段,最多1分钟没有任何变化。这是因为在删除目录时,FileSystemWatcher
会通知所有包含的文件和目录。我们可以通过吞下目录中包含的更改来优化行为,只需删除订阅者中的父级即可。这由PrioritizeFileSystemChanges
过滤器处理。它还允许我们忽略在缓冲区窗口中创建并随后删除的文件,再次减少目标上的IO操作。
虽然目前以一种天真的方式不支持失败/重试,但仍然有效。
但是我的问题是,此观察者的订阅者可能需要花费合理的时间来处理每个更改。例如,将大文件复制到慢速文件系统。当为当前正在复制的同一文件发生新的文件系统更改时,如何处理中止正在进行的操作。或者,如果文件包含在缓冲列表中但尚未完成,那么如何将其删除或排除?
我认为需要对原始observable进行另一次订阅,但我不确定如何最好地共享状态或修改挂起的任务?必须按接收顺序处理更改,这表示队列。但是,新的文件系统更改可能适用于需要取消或删除的排队操作。队列不是为无序删除而设计的。
例如,如果我们当前正在复制文件Foo\Bar.txt
并且删除了Foo
目录。然后,必须取消目录和所有子目录的任何正在进行或挂起的更改。这可能是任务并行库的一个用例,还是我可以采取一些反应式方法?
也会收到任何github拉取请求!
答案 0 :(得分:1)
您似乎在这里有几个目标/问题:
CancellationToken
并返回Task
(TPL)的方法。这里缺少的步骤似乎是如何从变化的“队列”转变为实际工作。基本上,订阅必须对更改进行排队(快速)并启动(慢速,异步)方法,如果它尚未运行,则“递归地”处理队列;类似的东西:
'changes is your returned observable
'toProcess is the "queue" of changes
'processor holds information about and the task of the in-progress operation
changes.Subscribe(Sub(c)
UpdateQueueWithChange(c, toProcess, processor)
If processor.Task.IsCompleted Then
ProcessNextChange(processor, toProcess)
End If
End Sub)
ProcessNextChange
是一种方法,它将获取队列中的下一个更改,启动操作,设置操作任务的回调以重新调用ProcessNextChange。如果没有留下任何更改,processor
应该被赋予一个完成的任务,该任务不会重新调用ProcessNextChange。
UpdateQueueWithChange
将需要更新“队列”并在必要时取消正在进行的操作,由于任务完成将触发对ProcessNextChange
的调用,这将启动下一个操作。
如果您想取消对可观察更改的订阅的取消操作,我建议将订阅一次性放入CompositeDisposable
以及存储SerialDisposable
的{{1}} }(由CancellationDispoable
更新并另外存储在ProcessNextChange
)中,这是操作方法所需的processor
的来源。
ProcessNextChange将检查SerialDisposable以查看它是否在启动操作之前已被处理。 CompositeDisposable将存储在某处以结束整个事情。
CancellationToken