簡體   English   中英

具有select到有狀態服務的Observable.Interval會導致奇怪的行為

[英]Observable.Interval with select to stateful service causes strange behaviour

我試圖使用Observable.Interval結合select來輪詢服務,這似乎是一種語法上很好的方法。 但是每當我嘗試實現一種等待observable完成的方法時,我會得到奇怪的行為,其中select多次調用的服務被調用。

沒有等待,我得到了正在尋找的正確行為......

private static ConcurrentQueue<string> _data = new ConcurrentQueue<string>(new [] { "a", "b", "c", "d" });

static void Main(string[] args)
{
    var observable = Observable
        .Interval(TimeSpan.FromSeconds(2))
        .Select(Transform)
        .TakeWhile(x => x != null);

    Console.WriteLine("Starting subcription");
    var disposable = observable.Subscribe(x => Console.WriteLine("Event raised for {0}", x));

    Console.WriteLine("Waiting for subcription to complete");
    // need to wait here

    Console.WriteLine("Press any key to exit. . .");
    Console.ReadKey();
}

private static string Transform(long x)
{
    string result;
    _data.TryDequeue(out result);

    Console.WriteLine("Transform invoked [x: {0}, Result: {1}]", x, result ?? "NULL");

    return result;
}

產量

Starting subcription
Waiting for subcription to complete
Press any key to exit. . .
Transform invoked [x: 0, Result: a]
Event raised for a
Transform invoked [x: 1, Result: b]
Event raised for b
Transform invoked [x: 2, Result: c]
Event raised for c
Transform invoked [x: 3, Result: d]
Event raised for d
Transform invoked [x: 4, Result: NULL]

如果我在observable上調用擴展方法wait,它似乎會導致每個間隔調用兩次Transform,並且只有一個值返回給事件...

private static ConcurrentQueue<string> _data = new ConcurrentQueue<string>(new [] { "a", "b", "c", "d" });

static void Main(string[] args)
{
    var observable = Observable
        .Interval(TimeSpan.FromSeconds(2))
        .Select(Transform)
        .TakeWhile(x => x != null);

    Console.WriteLine("Starting subcription");
    var disposable = observable.Subscribe(x => Console.WriteLine("Event raised for {0}", x));

    Console.WriteLine("Waiting for subcription to complete");
    observable.Wait();

    Console.WriteLine("Press any key to exit. . .");
    Console.ReadKey();
}

private static string Transform(long x)
{
    string result;
    _data.TryDequeue(out result);

    Console.WriteLine("Transform invoked [x: {0}, Result: {1}]", x, result ?? "NULL");

    return result;
}

產量

Starting subcription
Waiting for subcription to complete
Transform invoked [x: 0, Result: a]
Event raised for a
Transform invoked [x: 0, Result: b]
Transform invoked [x: 1, Result: c]
Event raised for c
Transform invoked [x: 1, Result: d]
Transform invoked [x: 2, Result: NULL]
Transform invoked [x: 2, Result: NULL]
Press any key to exit. . .

我懷疑這是因為Wait在幕后創建了第二個訂閱,我的observable后面的服務是有狀態的。

我見過人們建議使用ToTask來等待一個observable完成,這有着同樣奇怪的行為。

那么,在所有訂閱者接收相同數據集的同時,在觀察者后面輪詢有狀態服務的正確方法是什么?

幾件事:

  1. 建議僅在Subscribe期間更改狀態,而不是Select其他運算符。 我不確定這對你的例子是否實用。
  2. 您的observable是一個冷可觀察 ,意味着每個訂閱重新創建所有內容:后台每秒鍾滴答一個新的計時器,一個新的Select運算符等。您有兩個訂閱,一個來自Subscribe ,另一個來自Wait ,所以你' ll有兩個定時器,兩個Select運算符附加調用Transform

您可以通過以下兩種方式之一解決此問題:

  1. 將你的觀察結果變成一個熱的觀察者
  2. 消除訂閱(@Daniel Weber概述的解決方案)

您作為熱點可觀測量的可觀察量將如下所示:

var observable = Observable
    .Interval(TimeSpan.FromSeconds(2))
    .Select(Transform)
    .TakeWhile(x => x != null)
    .Publish()
    .RefCount();

我的建議是,既然你在你的observable中改變了狀態,我會很熱,以確保你最終沒有運行兩次。

確保您只訂閱了一次observable。 省略對Subscribe的第一次調用,然后將調用留給Wait 如果您仍想發出一些日志消息(正如您在訂閱中所做的那樣),請添加一個Do -step:

private static ConcurrentQueue<string> _data = new   ConcurrentQueue<string>(new [] { "a", "b", "c", "d" });

static void Main(string[] args)
{
    var observable = Observable
        .Interval(TimeSpan.FromSeconds(2))
        .Select(Transform)
        .TakeWhile(x => x != null);

    Console.WriteLine("Wait for the observable to complete.");
    observable
        .Do(x => Console.WriteLine("Event raised for {0}", x))
        .Wait();

    Console.WriteLine("Press any key to exit. . .");
    Console.ReadKey();
}

private static string Transform(long x)
{
    string result;
    _data.TryDequeue(out result);

    Console.WriteLine("Transform invoked [x: {0}, Result: {1}]", x, result ?? "NULL");

    return result;
}

請注意, Wait將阻塞(這在Main方法中是不可避免的)。 此外,當您的observable為空時它會拋出。 如果您對observable的任何值不感興趣,請添加LastOrDefault -step。

等待observable是一種固有的異步操作,所以你應該檢查是否可以使用ToTask而不是Wait並在異步方法中等待它。

暫無
暫無

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

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