C# 4.0 Rx TaskPoolScheduler与EventLoopScheduler,内存使用情况
我正在尝试对大量独立的数据源进行POC。一种经典的观察者风格的应用程序。数据源的数量可能从几百到几千不等,观察者的数量可能从2到20000不等。下面是一个简单的数据馈送可观察模型的快速示例:C# 4.0 Rx TaskPoolScheduler与EventLoopScheduler,内存使用情况,c#-4.0,system.reactive,reactive-programming,C# 4.0,System.reactive,Reactive Programming,我正在尝试对大量独立的数据源进行POC。一种经典的观察者风格的应用程序。数据源的数量可能从几百到几千不等,观察者的数量可能从2到20000不等。下面是一个简单的数据馈送可观察模型的快速示例: public class FeedMockUp { private readonly IScheduler observerScheduler; private readonly Random rnd = new Random((int)DateTime.No
public class FeedMockUp
{
private readonly IScheduler observerScheduler;
private readonly Random rnd = new Random((int)DateTime.Now.Ticks);
private readonly Subject<double> sourceObservable;
private readonly IObservable<double> testedObservable;
public FeedMockUp(IScheduler observerScheduler)
{
this.observerScheduler = observerScheduler;
sourceObservable = new Subject<double>();
testedObservable =
Observable.Create<double>(x =>
{
var underlyingSourceDisposable =
sourceObservable
.Subscribe(_ => x.OnNext(rnd.NextDouble()));
return underlyingSourceDisposable;
});
}
public IDisposable SubscribeToUnderlyingFeed(int numberOfSubscribers)
{
int counter = 0;
var disposable = new CompositeDisposable();
for (int i = 0; i < numberOfSubscribers; i++)
{
disposable.Add(testedObservable
.ObserveOn(observerScheduler)
.Subscribe(_ => Interlocked.Increment(ref counter)));
}
return disposable;
}
public void PushNewFeed()
{
sourceObservable.OnNext(rnd.NextDouble());
}
}
将
ObserveOn
与TaskPoolScheduler
一起使用,实质上是为每个观察者创建一个任务
默认的TaskScheduler
结束
每个线程为其堆栈保留大约1MB的内存
因此,使用TaskPoolScheduler
的500个观察者将保留至少500MB的空间。你可以看到这是怎么回事
另一方面,EventLoopScheduler
在单个线程上运行。因此,将ObserveOn
与此调度器一起使用,只需向调度器的工作队列添加一个条目即可。这个条目比一个线程的1MB开销小得多
因此,在这种情况下,EventLoopScheduler
的内存效率要高得多,但它也会连续通知观察者,如果有很多观察者,并且源以高频率生成,那么您将开始积累未发送事件的缓冲区
TaskPoolScheduler
的内存效率较低,但会同时通知观察者,因此可能会通过利用计算机上的所有内核来处理比EventLoopScheduler
频率更高的事件。您可能希望使用TaskPoolScheduler.Default.DisableOptimizations(typeof(IsSchedulerLongRunning))
。如果您不介意失去并行性,EventLoopScheduler是一个不错的选择
如果您仍然希望并行执行工作,但希望使用线程池线程,则最好使用此选项。您还可以为每个核心创建一个
EventLoopScheduler
,并在入口点上实现负载平衡器;例如,循环调度程序选择。这可以包装在其自己的IScheduler
implementat中ion,因此您只需要传递一个引用。当线程池开始增加时,看着内存呈指数级增长,这是非常有趣的。修复了该拼写错误。顺便说一句,Dave。此外,我在过去成功地使用了您建议的方法。
private static void Main(string[] args)
{
const int displayItemCount = 100;
const int callbackCount = 500;
//var rtScheduler = new EventLoopScheduler();
var rtScheduler = TaskPoolScheduler.Default;
var rtFeeds = new List<FeedMockUp>();
for (int i = 0; i < displayItemCount; i++)
{
var mockFeed = new FeedMockUp(rtScheduler);
mockFeed.SubscribeToUnderlyingFeed(callbackCount);
rtFeeds.Add(mockFeed);
}
foreach (var rtFeedMockUp in rtFeeds)
{
rtFeedMockUp.PushNewFeed();
}
Console.WriteLine("Memory used for feed {0} mockups with {1} observers / callbacks. Memory {2} Mb",
displayItemCount, callbackCount, Environment.WorkingSet / (1024 * 1024));
Console.ReadKey();
}