簡體   English   中英

熱觀察和IDisposable

[英]Hot observable and IDisposable

我想在hot observable和IDisposable對象上找到最佳實踐作為事件類型。

假設我的代碼將Bitmap對象生成為熱可觀察對象,並且我有幾個訂閱者。 例如:

    public static IObservable<Bitmap> ImagesInFolder(string path, IScheduler scheduler)
    {
        return Directory.GetFiles(path, "*.bmp")
            .ToObservable(scheduler)
            .Select(x => new Bitmap(x))
            .Publish()
            .RefCount();
    }

public void Main()
{
    var images = ImagesInFolder("c:\Users\VASIYA\Desktop\Sample Images", TaskPoolScheduler.Instance);
    var process1 = images.Subscribe(SaveBwImages);
    var process2 = images.Subscribe(SaveScaledImages);
    var process3 = images.Select(Cats).Subscribe(SaveCatsImages);
}

所以問題是:處理熱點可觀測資源的一次性資源的最佳實踐是什么?

在這個例子中,我想在使用后處理圖像,但我無法弄清楚 - 究竟是什么時候?

那個訂閱事件將被調用的情況並不明顯,所以我不能處理'last'事件。

提前致謝。

你的觀察不熱。 它是一個帶有共享源的冷可觀察對象,它只會使后續的觀察者表現得好像它們有一個熱的可觀察量。 它最好被描述為溫暖的可觀察者。

我們來看一個例子:

var query = Observable.Range(0, 3).ObserveOn(Scheduler.Default).Publish().RefCount();

query.Subscribe(x => { Thread.Sleep(1000); Console.WriteLine("A"); });
query.Subscribe(x => { Thread.Sleep(1000); Console.WriteLine("B"); });
query.Subscribe(x => { Thread.Sleep(1000); Console.WriteLine("C"); });

Thread.Sleep(10000);

query.Subscribe(x => { Thread.Sleep(1000); Console.WriteLine("D"); });

Observable
    .Range(0, 3)
    .ObserveOn(Scheduler.Default)
    .Publish()
    .RefCount()
    .Subscribe(x => { Thread.Sleep(1000); Console.WriteLine("E"); });

當我運行這個時,我得到:

A
A
B
C
A
B
C
E
E
E

“B”和“C”觀察者錯過了序列的第一個值。

並且,在完成“A”,“B”和“C”觀察者之后,序列結束,因此“D”永遠不會得到值。 我必須創建一個全新的observable才能顯示值“E”。

所以,在你的代碼中你有一個問題,如果第一個觀察者在第二個和第三個訂閱之前完成一個或多個值,那么那些觀察者會錯過值。 那是你要的嗎?

然而,你的問題是關於如何處理從一個可觀察者返回的一次性價值。 如果您使用Observable.Using這很簡單。

這與您的代碼類似:

public static IObservable<IDisposable> ImagesInFolder(IScheduler scheduler)
{
    return
        Observable
            .Range(0, 3)
            .ObserveOn(Scheduler.Default)
            .SelectMany(x =>
                Observable
                    .Using(
                        () => Disposable.Create(() => Console.WriteLine("Disposed!")),
                        y => Observable.Return(y)))
        .Publish()
        .RefCount();
}

現在,如果我運行此代碼:

var query = ImagesInFolder(Scheduler.Default);

query.Subscribe(x => { Thread.Sleep(1000); Console.WriteLine("A"); });
query.Subscribe(x => { Thread.Sleep(1000); Console.WriteLine("B"); });
query.Subscribe(x => { Thread.Sleep(1000); Console.WriteLine("C"); });

Thread.Sleep(10000);

query.Subscribe(x => { Thread.Sleep(1000); Console.WriteLine("D"); });

我得到這個輸出:

A
B
C
Disposed!
A
B
C
Disposed!
A
B
C
Disposed!

同樣“D”永遠不會產生任何值 - 並且“B”和“C”可能會錯過值,但這確實顯示了如何返回一個可觀察的值,該值在觀察者完成后自動處理掉。

您的代碼如下所示:

public static IObservable<System.Drawing.Bitmap> ImagesInFolder(string path, IScheduler scheduler)
{
    return
        Directory
            .GetFiles(path, "*.bmp")
            .ToObservable(scheduler)
            .SelectMany(x =>
                Observable
                    .Using(
                        () => new System.Drawing.Bitmap(x),
                        bm => Observable.Return(bm)))
        .Publish()
        .RefCount();
}

但是,你仍然處於可能缺失價值的土地上。

因此你需要真正做到這一點:

public static IConnectableObservable<System.Drawing.Bitmap> ImagesInFolder(string path, IScheduler scheduler)
{
    return
        Directory
            .GetFiles(path, "*.bmp")
            .ToObservable(scheduler)
            .SelectMany(x =>
                Observable
                    .Using(
                        () => new System.Drawing.Bitmap(x),
                        bm => Observable.Return(bm)))
            .Publish();
}

然后你這樣稱呼它:

public void Main()
{
    var images = ImagesInFolder("c:\Users\VASIYA\Desktop\Sample Images", TaskPoolScheduler.Instance);
    var process1 = images.Subscribe(SaveBwImages);
    var process2 = images.Subscribe(SaveScaledImages);
    var process3 = images.Select(Cats).Subscribe(SaveCatsImages);
    images.Connect();
}

另一個選項是刪除整個.Publish().RefCount()代碼,並確保在訂閱時自己正確執行。

試試這段代碼:

void Main()
{
    ImagesInFolder(Scheduler.Default)
        .Publish(iif =>
            Observable
                .Merge(
                    iif.Select(x => { Thread.Sleep(1000); Console.WriteLine("A"); return "A"; }),
                    iif.Select(x => { Thread.Sleep(3000); Console.WriteLine("B"); return "B"; }),
                    iif.Select(x => { Thread.Sleep(2000); Console.WriteLine("C"); return "C"; })))
        .Subscribe();
}

public static IObservable<IDisposable> ImagesInFolder(IScheduler scheduler)
{
    return
        Observable
            .Range(0, 3)
            .ObserveOn(Scheduler.Default)
            .SelectMany(x =>
                Observable
                    .Using(
                        () => Disposable.Create(() => Console.WriteLine("Disposed!")),
                        y => Observable.Return(y)));
}

我明白了:

A
B
C
Disposed!
A
B
C
Disposed!
A
B
C
Disposed!

再次,一個Disposed! 在每個觀察者運行之后,現在的問題是我改變了每個觀察者處理的延遲,但代碼仍然是輸出觀察者的順序。 問題是Rx按順序運行每個觀察者,並且每個生成的值都是順序的。

我希望您認為可以使用.Publish()並行處理。 你沒有。

.Publish()並行運行的方法是完全刪除.Publish()

做這種事:

void Main()
{
    ImagesInFolder(Scheduler.Default).Subscribe(x => { Thread.Sleep(1000); Console.WriteLine("A"); });
    ImagesInFolder(Scheduler.Default).Subscribe(x => { Thread.Sleep(3000); Console.WriteLine("B"); });
    ImagesInFolder(Scheduler.Default).Subscribe(x => { Thread.Sleep(2000); Console.WriteLine("C"); });
}

public static IObservable<IDisposable> ImagesInFolder(IScheduler scheduler)
{
    return
        Observable
            .Range(0, 3)
            .ObserveOn(Scheduler.Default)
            .SelectMany(x =>
                Observable
                    .Using(
                        () => Disposable.Create(() => Console.WriteLine("Disposed!")),
                        y => Observable.Return(y)));
}

我現在得到這個:

A
Disposed!
C
Disposed!
A
Disposed!
B
Disposed!
A
Disposed!
C
Disposed!
C
Disposed!
B
Disposed!
B
Disposed!

代碼現在並行運行並盡快完成 - 並在訂閱完成時正確處理IDisposable 你只是沒有得到與每個觀察者共享一個可支配資源的樂趣,但你也沒有得到所有的行為問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM