C#Rx可观测产生随机结果

C#Rx可观测产生随机结果,c#,observable,system.reactive,C#,Observable,System.reactive,考虑以下程序 类程序 { 静态IObservable GetNumbers() { var observable=observable.Empty(); foreach(可枚举范围(1,10)中的VarI) { observable=observable.Concat(observable.FromAsync(()=>Task.Run(()=> { Console.WriteLine($“生成{i}”); 睡眠(1000); 返回i; }))); } 可观测收益; } 静态异步任务日志号(IOb

考虑以下程序

类程序
{
静态IObservable GetNumbers()
{
var observable=observable.Empty();
foreach(可枚举范围(1,10)中的VarI)
{
observable=observable.Concat(observable.FromAsync(()=>Task.Run(()=>
{
Console.WriteLine($“生成{i}”);
睡眠(1000);
返回i;
})));
}
可观测收益;
}
静态异步任务日志号(IObservable-observable)
{
var subscription=observable.subscripte(i=>Console.WriteLine($“Consuming{i}”);
等待观察;
subscription.Dispose();
}
静态void Main(字符串[]参数)
{
LogNumbers(GetNumbers()).Wait();
控制台。写入线(“完成”);
Console.ReadLine();
}
}
它产生以下输出

Producing 1
Producing 1
Producing 2
Consuming 1
Producing 2
Producing 3
Consuming 2
Producing 3
Producing 4
Consuming 3
Producing 4
Producing 5
Consuming 4
Producing 5
Producing 6
Consuming 5
Producing 6
Producing 7
Consuming 6
Producing 7
Producing 8
Consuming 7
Producing 8
Producing 9
Consuming 8
Producing 9
Producing 10
Consuming 9
Producing 10
Finished

它写出两条“生成x”语句和一条“消费x”语句。为什么会这样?为什么它从来没有写出预期的最终“消费10”声明呢?

因为您订阅了两次,所以您将获得两份生产线副本。最有可能的情况是,您没有获得消费10,因为第一次订阅将在第二次订阅结束时取消。如果你有时会因为任务运行的顺序不同而得到10,我不会感到惊讶

static async Task LogNumbers(IObservable<int> observable)
{
    //This is the first subscription
    var subscription = observable.Subscribe(i => Console.WriteLine($"Consuming {i}"));

    //This is the second subscription
    await observable;

    subscription.Dispose();
}

Gideon为您解决了这个问题,但当我开始在评论中添加一些提示时,我认为发布一个完整的解决方案可能会更好。试试这个:

static IObservable<int> GetNumbers() =>
    Observable
        .Interval(TimeSpan.FromSeconds(1.0))
        .Select(i => (int)i + 1)
        .Do(i => Console.WriteLine($"Producing {i}"))
        .Take(10);

static Task LogNumbers(IObservable<int> observable) =>
    observable
        .Do(i => Console.WriteLine($"Consuming {i}"))
        .ToArray()
        .ToTask();

static void Main(string[] args)
{
    LogNumbers(GetNumbers()).Wait();
    Console.WriteLine("Finished");
    Console.ReadLine();
}
static IObservable GetNumbers()=>
可观察
.间隔(时间跨度从秒(1.0))
.选择(i=>(int)i+1)
.Do(i=>Console.WriteLine($“生成{i}”))
.采取(10);
静态任务日志号(IObservable-observable)=>
可观察
.Do(i=>Console.WriteLine($“消费{i}”))
.ToArray()
.ToTask();
静态void Main(字符串[]参数)
{
LogNumbers(GetNumbers()).Wait();
控制台。写入线(“完成”);
Console.ReadLine();
}
或者更清楚地说:

static IObservable<int> GetNumbers() =>
    Observable
        .Interval(TimeSpan.FromSeconds(1.0))
        .Select(i => (int)i + 1)
        .Do(i => Console.WriteLine($"Producing {i}"))
        .Take(10);

static IObservable<int> LogNumbers(IObservable<int> observable) =>
    observable
        .Do(i => Console.WriteLine($"Consuming {i}"));

static async Task Main(string[] args)
{
    await LogNumbers(GetNumbers());
    Console.WriteLine("Finished");
    Console.ReadLine();
}
static IObservable GetNumbers()=>
可观察
.间隔(时间跨度从秒(1.0))
.选择(i=>(int)i+1)
.Do(i=>Console.WriteLine($“生成{i}”))
.采取(10);
静态IObservable LogNumber(IObservable observable)=>
可观察
.Do(i=>Console.WriteLine($“消费{i}”);
静态异步任务主(字符串[]args)
{
等待日志号(GetNumbers());
控制台。写入线(“完成”);
Console.ReadLine();
}

您可以
直接等待
可观察数据。

您将concat(observable.concat)数据放在一起,而不是清除可观察数据。在可能的情况下,一个好的经验法则是“不要混合您的单子”。这适用于同时使用任务和接收。不要使用
Observable.fromsync(()=>Task.Run(()=>i))
您只需使用
Observable.Start(()=>i)
。尽量留在Rx世界。这并不能解决您的问题,但会使您的Rx代码更加健壮。@Enigmativity可以理解,谢谢。我只是想增加一些延迟来重现问题。如果速度太快,第二个问题就不会发生。我的实际代码与此不同,但如果我st说明@Steztric-使用
Observable.Delay
添加延迟。:-@Steztric-如下:
Observable.Return(42.Delay)(TimeSpan.FromSeconds(5.0))
。方法
LogNumbers
可能比
Task
更好的返回类型是
Task
(关于第一个示例)。@TheodorZoulias-同意。我本想保留OP的签名,但你已经成功了。谢谢@Enigmativity,这真的很有用。我想这个
是可以观察到的。间隔(…)
可以用于定期发布度量或对web服务进行某种轮询,对吗?@Steztric-是的,它相当于
计时器。它定期发送一个值。
static IObservable<int> GetNumbers() =>
    Observable
        .Interval(TimeSpan.FromSeconds(1.0))
        .Select(i => (int)i + 1)
        .Do(i => Console.WriteLine($"Producing {i}"))
        .Take(10);

static IObservable<int> LogNumbers(IObservable<int> observable) =>
    observable
        .Do(i => Console.WriteLine($"Consuming {i}"));

static async Task Main(string[] args)
{
    await LogNumbers(GetNumbers());
    Console.WriteLine("Finished");
    Console.ReadLine();
}