我最近加入了Rx,并且我正在使用它来帮助我从数据挖掘应用程序中的几个API中提取数据。
我有一个为每个API实现的接口,它封装了对每个API的公共调用,例如
public interface IMyApi {
IObservable<string> GetApiName(); //Cold feed for getting the API's name.
IObservable<int> GetNumberFeed(); //Hot feed of numbers from the API
}
我的问题是关于冷IObservables vs Tasks。在我看来,冷可观察基本上是一项任务,它们的运作方式大致相同。让我感到奇怪的是,抽象&#39;当你可以争辩说任务就是你所需要的时候,任务就像一个冷酷的观察者。同时使用cold observable来包装Tasks会隐藏活动的性质,因为签名看起来与热的可观察性相同。
我可以代表上述界面的另一种方式是:
public interface IMyApi {
Task<string> GetApiNameAsync(); //Async method for getting the API's name.
IObservable<int> GetNumberFeed(); //Hot feed of numbers from the API
}
对于为什么我不应该在任务和IObservable之间混合搭配,有一些传统的智慧吗?
编辑:澄清 - 我已经阅读了其他发布的讨论并理解了Rx和TPL之间的关系,但我的担忧主要在于它是否安全到将两者结合在一个应用程序中,是否会导致不良做法或线程和调度陷阱?
答案 0 :(得分:5)
我的问题是关于冷IObservables vs Tasks。在我看来,冷可观察性基本上是一项任务,它们的运作方式大致相同
重要的是要注意,这是不的情况,它们是非常不同的。这是核心差异:
// Nothing happens here at all! Just like calling Enumerable.Range(0, 100000000)
// doesn't actually create a huge array, until I use foreach.
var myColdObservable = MakeANetworkRequestObservable();
// Two network requests made!
myColdObservable.Subscribe(x => /*...*/);
myColdObservable.Subscribe(x => /*...*/);
// Only ***one*** network request made, subscribers share the
// result
var myTaskObservable = MakeATask().ToObservable();
myTaskObservable.Subscribe(x => /*...*/);
myTaskObservable.Subscribe(x => /*...*/);
为什么这很重要? Rx中的几个方法(例如Retry
)取决于此行为:
// Retries three times, then gives up
myColdObservable.Retry(3).Subscribe(x => /*...*/);
// Actually *never* retries, and is effectively the same as if the
// Retry were never there, since all three tries will get the same
// result!
myTaskObservable.Retry(3).Subscribe(x => /*...*/);
所以一般来说,将你的Observable作为 cold 通常会让你的生活更轻松。
使用Defer运算符:
var obs = Observable.Defer(() => CreateATask().ToObservable());
// CreateATask called *twice* here
obs.Subscribe(/*...*/);
obs.Subscribe(/*...*/);
答案 1 :(得分:3)
Task
和IObservable
之间的区别并不热与冷:Task
- 返回方法几乎可以&#34;冷&#34; (每次通话都会返回新的Task
)或“#34; hot&#34; (总是返回相同的Task
),就像IObservable
s。
两者之间的区别在于IObservable
代表结果序列,而Task
代表单一结果。
因此,如果您总是只有一个结果(或错误),请使用Task
,当您可以获得任意数量的结果时,请使用IObservable
。
答案 2 :(得分:3)
混合模型没有问题,事实上即使是Rx团队也在Rx中包含了许多自适应运算符。例如,ToTask
,ToObservable
,SelectMany
,DeferAsync
,StartAsync
,ToAsync
等。您甚至可以await
IObservable<T>
方法中的async
。
应该影响您决定的主要区别是基数:
IObservable<T>
是[0,∞]
Task<T>
是[0,1]
因此,如果您只需要表示一个返回值,那么请强烈考虑使用Task<T>
。