Ich schreibe eine C# (.NET 4.5) Anwendung, die verwendet wird, um zeitbasierte Ereignisse für Berichtszwecke zu aggregieren. Um meine Abfragelogik für Echtzeit- und Verlaufsdaten wiederverwendbar zu machen, nutze ich die Reaktive Erweiterungen (2.0) und ihre IScheduler
Infrastruktur (HistoricalScheduler
und Freunde).Warum wirft Observable.Generate() System.StackOverflowException?
Zum Beispiel nehmen wir eine Liste von Ereignissen erstellen (chronologisch sortiert, aber sie können zusammenfallen!), Deren einzige Nutzlast ist ihre Zeitstempel und wollen ihre Verteilung über Puffer von befristetem wissen:
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));
Ausführen dieses Codes führt zu einem System.StackOverflowException
mit dem folgenden Stack-Trace (Es sind die letzten drei Linien den ganzen Weg nach unten):
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, scheint das Problem aus meiner Verwendung von Observable.Generate()
zu kommen, auf der Liste abhängig Größe (num
) und unabhängig von der Wahl des Schedulers.
Was mache ich falsch? Oder allgemeiner, was wäre der bevorzugte Weg, um eine IObservable
von einer IEnumerable
von Ereignissen zu erstellen, die ihre eigenen Zeitstempel bieten?
Wie groß kann 'num' sein, bevor Sie auf diesen Fehler stoßen? Wenn Sie dies im Debugger in einem Schritt ausführen, wird die letzte Codezeile ausgeführt, bevor der Fehler angezeigt wird. –
Für mich scheint der kritische Schwellenwert bei ~ 'num = 51600' zu liegen (in Release config, ein bisschen weniger in Debug config). Die beobachtbare Sequenz scheint vollständig erstellt zu sein. Ich kann Haltepunkte bei den lamdba-Ausdrücken für "Observable.Generate()" treffen. Die Ausnahme wird nach dem letzten Aufruf von 'Console.WriteLine()' ausgelöst. –
Verstehen Sie, dies ist nur eine Vermutung, aber es sieht verdächtig aus, dass der Stream versucht, jedes Element zu entfernen, und jedes Element versucht, den Stream zu entsorgen. Sie enden damit, was im Wesentlichen rekursive Aufrufe zu "Abbrechen" oder "Dispose" sind, die Ihren Stack (Standardgröße von 1 Megabyte) bläst. Ich kenne Observable nicht genug, um zu sagen, warum das passiert. –