Subject.OnNext(value)一次向观察者发出多次通知(对于相同的更改值)

时间:2014-01-09 08:54:33

标签: c# .net visual-studio system.reactive reactive-programming

行为真的很奇怪,它看起来像框架本身的一个大错误,

我有一个FileSystemWatcher来检测文件夹中的新文件,每次检测到新文件时,都会在发送Rx .OnNext通知之前解析并删除该文件:

private Subject<MyObject> objectNotification = new Subject<MyObject>();
private FileSystemWatcher watcher;

private void MyClassConstructor(string pathToWatch)
{
    watcher= new FileSystemWatcher();
    watcher.Filter = scraper.Ext;
    watcher.Created += new FileSystemEventHandler(parseMethod);
    watcher.Path = pathToWatch;
    watcher.EnableRaisingEvents = true;
}

private void parseMethod(object sender, FileSystemEventArgs e)
{
    MyObject parsedFile = new MyObject(e.FullPath);
    File.Delete(e.FullPath);
    var syncedSubject = Subject.Synchronize(objectNotification);
    syncedSubject.OnNext(parsedFile);
}

另一方面,在主应用程序的GUI代码中的某处有接收代码:

private IDisposable myObjectSubscription;
.
.
.
private void initSubscription()
{
    this.myObjectSubscription = sendingClass.Subscribe(parsedObject =>
    {
      this.AddObject(parsedObject);
    });
}

private void addObject(MyObject parsedObject)
{
    //Sometimes the same object is being received many times, but has been sent only once!
    //BUG!
}

理想情况下,我应该在观察者中收到发送者类发送的每个值的通知,不幸的是我得到更多,更多!

1 个答案:

答案 0 :(得分:2)

可观察同步是这个问题的核心问题。

存在Subject或Observable同步方法以防止进行并发OnXXX调用。

因为同步在没有必要时引入了开销(这通常就足够了),并且由于下面描述的替代方法,Rx API的用户需要决定何时以及如何同步。

在您的情况下,每次引发事件并调用事件处理程序时,parseMethod都会为该事件创建一个新的同步主题。这不是您想要的,也不会阻止并发OnNext()次呼叫。因此,由于观察者OnNext()处理程序中的并发调用,您会看到竞争条件错误。

不是在parseMethod中创建同步主题,而是在构造函数中执行或者在订阅上进行同步,它应该可以工作。

e.g。 :

要么这样做:

private ISubject<MyObject,MyObject> objectNotification =
    Subject.Synchronized(new Subject<MyObject>());

...

private void parseMethod(object sender, FileSystemEventArgs e)
{
    MyObject parsedFile = new MyObject(e.FullPath);
    File.Delete(e.FullPath);
    objectNotification.OnNext(parsedFile);
}

或者这样做:

private void initSubscription()
{
    this.myObjectSubscription = sendingClass.Synchronize().Subscribe(parsedObject =>
    {
      this.AddObject(parsedObject);
    });
}

请注意,如果有多个订阅者,这些替代方案会有不同的行为!

在第一种情况下,您确保在所有观察者中至少​​有一个OnNext()在飞行中。第一个事件发送到第一个订户,然后发送到第二个订户...直到所有订户都收到该事件,然后发送第二个事件等。

在第二种情况下,您会介绍一些(通常是安全的)并发性 - 现在您确保在特定事件中 中最多只有一个OnNext() 所有观察员。换句话说,可以对 不同观察者 中的 不同事件 进行并发OnNext()次调用,但只有 一个 OnNext()对特定事件的调用才会在任何时候进行,并且不会对同一个观察者进行并发调用。

您更喜欢哪个,后者允许观察者并行运行,但仍会阻止您看到的问题;哪种方法更好取决于上面未提供的实施细节。