如何“门控”一个可观察的对象

时间:2019-07-15 08:56:06

标签: c# observable system.reactive

我有一个Observable流,每次文件监视程序检测到发生更改时,该流就会被打勾。然后,此勾号将导致嵌套的Observable开始,以重复读取打开的文件流,直到读取所有新更新,然后将它们展平为最终输出流。

我的问题是,如果文件发生更改,则启动嵌套的Observable,如果此嵌套的Observable仍在读取文件中的行,并且在文件发生第二次更改时尚未完成,则文件观察器可观察到会导致SECOND嵌套可观察到的事件开始,因此您有两个嵌套可观察到的对象都试图从同一流中读取,这会导致InvalidOperationException-流正在被另一个进程使用

如何更改此Observable,使其仅在上一个Observable完成时才允许父Observable创建嵌套的Observable?就是说,像手动重置事件那样控制可观察的父对象,它仅允许它在先前的嵌套操作完成并停止使用流后才能继续?

我的代码

    var fileSystemWatcherChanges = Observable<Unit>()

/// abbreviated for question, code here opens a file watcher to create this stream

    var outputStream = fileSystemWatcherChanges
                                        .StartWith(Unit.Default)
                                        .Select(x =>
                                            Observable
                                                .Defer(() => Observable.FromAsync(sr.ReadLineAsync))
                                                .Repeat()
                                                .TakeUntil(w => w == null))
                                        .Merge()
                                        .Where(w => w != null))

使用解决方案更新:

对于那些有兴趣解决此问题的人,我已经做了一些基本的锁定,看来已经解决了这个问题。如果我们当前仍在处理内部可观察的内容,它将忽略滴答声,在这种情况下,我相信这是令人满意的,因为更新滴答声仅告诉我们有更多行需要读取,而当前正在执行的ReadLine observable应该选择这些新行如果他们在那里。在我们积极处理文件流时,我们不在乎任何文件更新触发器。

var readingFile = 0;            
var outputStream = fileSystemWatcherChanges
    .StartWith(Unit.Default)
    .Select(delegate(Unit x)
    {
        // Deny a second file update from starting to read the stream if we are already reading it
        if (0 == Interlocked.Exchange(ref readingFile, 1))
        {
            return Observable
                .Defer(() => Observable.FromAsync(sr.ReadLineAsync))
                .Repeat()
                .TakeUntil(w => w == null)
                .Finally(() => Interlocked.Exchange(ref readingFile, 0));
        }

        return Observable.Empty<string>();
    })
    .Merge()
    .Where(w => w != null))

1 个答案:

答案 0 :(得分:0)

您应该使用Merge而不是Concat,它会订阅下一个可观察到的对象,直到上一个观察完成为止:

public static IObservable<string> ReadAsObservable<TNotification>(this TextReader reader, IObservable<TNotification> notification)
{
    return Observable.Using(() => reader,
        r =>
        {
            var readLine = Observable.FromAsync(r.ReadLineAsync);
            return notification.Select(_ => readLine.Repeat().TakeWhile(x => x != null))
                               .Concat();
        });
}