Piszę aplikację C# (.NET 4.5), która służy do agregowania zdarzeń opartych na czasie do celów raportowania. Aby moja logika kwerend mogła być ponownie używana zarówno w czasie rzeczywistym, jak iw danych historycznych, korzystam z infrastruktury Reactive Extensions (2.0) i ich infrastruktury IScheduler
(i znajomych).Dlaczego metoda Observable.Generate() generuje wyjątek System.StackOverflowException?
Na przykład załóżmy, tworzymy listę zdarzeń (chronologicznie, ale mogą pokrywać się!), Której jedynym ładowność ist ich znacznika czasu i chcesz poznać ich dystrybucji w całej buforów ustalony czas trwania:
const int num = 100000;
const int dist = 10;
var events = new List<DateTimeOffset>();
var curr = DateTimeOffset.Now;
var gap = new Random();
var time = new HistoricalScheduler(curr);
for (int i = 0; i < num; i++)
{
events.Add(curr);
curr += TimeSpan.FromMilliseconds(gap.Next(dist));
}
var stream = Observable.Generate<int, DateTimeOffset>(
0,
s => s < events.Count,
s => s + 1,
s => events[s],
s => events[s],
time);
stream.Buffer(TimeSpan.FromMilliseconds(num), time)
.Subscribe(l => Console.WriteLine(time.Now + ": " + l.Count));
time.AdvanceBy(TimeSpan.FromMilliseconds(num * dist));
uruchomiony ten kod wyniki w System.StackOverflowException
z poniższego śladu stosu (its ciągu ostatnich 3 linie w dół):
mscorlib.dll!System.Threading.Interlocked.Exchange<System.IDisposable>(ref System.IDisposable location1, System.IDisposable value) + 0x3d bytes
System.Reactive.Core.dll!System.Reactive.Disposables.SingleAssignmentDisposable.Dispose() + 0x37 bytes
System.Reactive.Core.dll!System.Reactive.Concurrency.ScheduledItem<System.DateTimeOffset>.Cancel() + 0x23 bytes
...
System.Reactive.Core.dll!System.Reactive.Disposables.AnonymousDisposable.Dispose() + 0x4d bytes
System.Reactive.Core.dll!System.Reactive.Disposables.SingleAssignmentDisposable.Dispose() + 0x4f bytes
System.Reactive.Core.dll!System.Reactive.Concurrency.ScheduledItem<System.DateTimeOffset>.Cancel() + 0x23 bytes
...
Ok, problem wydaje się pochodzić z mojego użytkowania Observable.Generate()
, w zależności od listy rozmiar (num
) i niezależnie od wyboru programu planującego.
Co robię źle? Lub bardziej ogólnie, jaki byłby preferowany sposób tworzenia IObservable
z wydarzeń, które dostarczają własne znaczniki czasu?
Jak duży może być "num", zanim wystąpi ten błąd? Co więcej, jeśli rozwiążesz ten problem w debugerze, jaki jest ostatni wiersz kodu wykonywany przed pojawieniem się błędu? –
Dla mnie próg krytyczny wydaje się mieć wartość ~ 'num = 51600' (w wersji Release config, nieco mniej w konfiguracji debugowania). Obserwowalna sekwencja wydaje się być całkowicie stworzona. Mogę trafić punkty przerwania w wyrażeniach lamdba dla 'Observable.Generate()'. Wyjątek zostanie zgłoszony po ostatnim wywołaniu 'Console.WriteLine()'. –
Zrozum, to tylko przypuszczenie, ale wygląda podejrzanie, tak jak strumień próbuje pozbyć się każdego elementu, a każdy element próbuje zrzucić strumień. Kończy się to, co w istocie wywołuje rekursywne wezwania do "Cancel" lub "Dispose", które dmucha twój stack (domyślny rozmiar to 1 megabajt). Nie jestem wystarczająco obeznany z "Observable", aby powiedzieć, dlaczego tak się dzieje. –