我正在使用Reactive Extensions创建匿名Observable来执行Web请求,对其进行后处理并返回一些值。这种技术帮助我将价值获取的机制与使用这些价值的其他类别分开。 Web请求经常执行,因此有一个使用此技术构造和调用请求的计时器。我按如下方式构建了Observable:
Observable.FromAsyncPattern<WebResponse>(getRequest.BeginGetResponse, getRequest.EndGetResponse)
实际上,端点并不总是可用。在Web请求超时的情况下,永远不会调用Subscribe()
方法中指定的回调。我正在使用Timeout()
Rx扩展来处理这种情况。另外我正在对请求结果进行后处理。以下是我为解决此问题而实施的仿真代码:
var request = HttpWebRequest.Create("http://10.5.5.137/unreachable_endpoint");
var obs = Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse)();
var disposable = obs.Select(res => res.ContentLength).Timeout(TimeSpan.FromSeconds(2)).Subscribe(...)
然后我实现了处理Subscribe()
方法的返回值以从Observable取消订阅回调委托。实际上我有一组三元组<IObservable obs, IDisposable disp, bool RequestComplete>
。 Subscribe()
方法回调设置RequestComplete = true
并且随着时间的推移,如果不再需要Disposed,则所有一次性使用Disposed,即已经调用了回调。一切似乎都很好,但我注意到收藏品不断增长,我不知道为什么。
所以我实现了这种情况的模拟。这段代码没有处理部分 - 我用简单的请求计数器替换它:
public static class RequestCounter
{
private static object syncRoot = new object();
private static int count = 0;
public static void Increment()
{
lock (syncRoot)
{
count++;
}
}
public static void Decrement()
{
lock (syncRoot)
{
count--;
}
}
public static int RequestCount
{
get { return count; }
}
}
这是测试本身:
public void RunTest()
{
while (true)
{
var request = HttpWebRequest.Create("http://10.5.5.137/unreachable_endpoint");
var obs = Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse)();
RequestCounter.Increment();
var disposable = obs.Select(res => res.ContentLength).Timeout(TimeSpan.FromSeconds(2)).Subscribe(
res =>
{
RequestCounter.Decrement();
Console.WriteLine("Pending requests count: {0}", RequestCounter.RequestCount);
},
ex =>
{
RequestCounter.Decrement();
Console.WriteLine("Pending requests count: {0}", RequestCounter.RequestCount);
},
() =>
{
});
}
}
在Web请求计数器增加之前。请求超时后,它会递减。但请求计数器增长无限。你能解释一下我错过了什么吗?
答案 0 :(得分:1)
对可观察序列的订阅是异步的。你的while循环尽可能快地继续前进。增量将超过延迟减量。
如果您在.NET 4.5中使用Rx v2.0,则可以使用await运行循环,并在上一个请求完成时继续执行以下迭代:
while (true)
{
var obs = ...;
RequestCounter.Increment();
await obs.Select(...).Timeout(...);
RequestCounter.Decrement();
Console.WriteLine(...);
}
更大的问题是,如果请求完成,您为什么要尝试处理订阅?当序列到达终端状态(OnError,OnCompleted)时,Rx会自行清理,所以看起来你可以简单地将IDisposable对象放在地板上而不用担心它们。