Autor Beitrag
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Sa 12.07.14 01:35 
Servus,

ich spiele gerade ein bisschen mit dem Dispatcher rum und habe damit mal ganz simpel Zahlen in die Konsole schreiben lassen.

Mein Code dazu sieht so aus:

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
static void Main(string[] args)
{
    var dispatcher = CreateDispatcher();

    for (int i = 0; i < int.MaxValue; i++)
    {
        var operation = dispatcher.BeginInvoke(new Action<int>(x =>
        {
            Console.WriteLine(i);
        }), new object[] { i });
    }

    Console.WriteLine("{0}{0}{0}{0}Ende", Environment.NewLine);
    Console.ReadKey();

    dispatcher.BeginInvokeShutdown(DispatcherPriority.Normal);
}

static Dispatcher CreateDispatcher()
{
    Dispatcher dispatcher = null;
    var dispatcherReadyEvent = new ManualResetEvent(false);

    new Thread(new ThreadStart(() =>
    {
        dispatcher = Dispatcher.CurrentDispatcher;
        dispatcherReadyEvent.Set();
        Dispatcher.Run();
    })).Start();

    dispatcherReadyEvent.WaitOne();
    return dispatcher;
}


Nun stellen sich mir aber zwei Fragen:

Wenn ich bei BeginInvoke nicht mehr den Parameter x in die Konsole schreibe, sondern i, dann kann ich davon aus gehen, dass die Zahlen nicht mehr in der richtigen Reihenfolge geschrieben werden, da die Schleife deutlich schneller ist und für jeden Zählvorgang nicht die Zeit bleibt um das auch noch in die Konsole zu schreiben.
Aber warum ist das so viel schneller? Der hat schon bis mehrere Millionen hoch gezählt, während ich gerade mal 10000 lese, wenn ich den Parameter x schreibe. Sieht das nur so aus, als würde es schneller hoch zählen, oder ist das tatsächlich schneller und woran liegt das?

Meine zweite Frage:
Warum läut das Program immer in eine OutOfMemoryException?
Der Prozess hat dann ungefähr 1,7 GB RAM belegt. Alleine das finde ich seltsam, hält der tatsächlich jede Zahl, die in der Konsole steht, im Speicher?
Ich habe daher mal Console.BufferHeight halbiert, allerdings bringt das keine Verbesserung und eine andere vergleichbare Property habe ich nicht gefunden.
Woran liegt das, wird hier einem .Net-Prozess eine Sperre aufgezwängt?


Gruß
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 12.07.14 08:21 
Wenn du ein wenig langsamer machst, klappt das:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
            for (int i = 0; i < int.MaxValue; i++)
            {
                if (i % 100 == 0)
                    Thread.Sleep(10);

                var operation = dispatcher.BeginInvoke(new Action<int>(x =>
                {
                    Console.WriteLine(i);
                }), new object[] { i });
            }
Das ist genau ein Grund weshalb ich Garbage Collection nicht mag. Man weiß nicht wann Speicher wieder freigegeben wird und wenn nicht warum nicht...
Da helfen dann nur noch Memory Profiler und beten.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4798
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Sa 12.07.14 10:42 
Hallo Palladin007,

mittels Dispatcher.BeginInvoke legst du den Methodenaufruf ja ersteinmal in die Warteschlange (Queue) der GUI-Thread MessageLoop - und diese ist im Gegensatz zu deiner Zählschleife natürlich um einiges langsamer.
Bei Dispatcher.Invoke wird dagegen synchron die Methode abgearbeitet.

Und die OutOfMemoryException erhältst du, weil du eben die Queue "vollballerst".
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 12.07.14 11:26 
Das dachte ich eigentlich auch, aber ich habe in der Schleife und im Delegate jeweils den aktuellen Wert ausgeben lassen. Der stimmt fast überein.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Sa 12.07.14 11:41 
Die Exception erfolgt weil das erzeugen und einreihen neuer Actions via BeginInvoke schneller ist als das abarbeiten der Action. Die Console ist unendlich langsam verglichen mit Code.
Da das das Einreihen schneller als das abarbeiten ist wird die interne Queue (mit den DispatcherOperations die BeginInvoke zurückliefert) des Dispatchers vermutlich überlaufen.
OutOfMemory beutetet auch nicht zwingend das es keinen Speicher mehr gibt sondern nur das kein neuer Speicher angefordert werden kann. Das kann andere Gründe haben (Fragmentierung etc.)


Zitat:
Warteschlange (Queue) der GUI-Thread MessageLoop

Hier gibt es keine GUI Thread und keine MessageLoop ist offensichtlich eine reine Konsolenanwendung ;)

Zitat:
Das ist genau ein Grund weshalb ich Garbage Collection nicht mag. Man weiß nicht wann Speicher wieder freigegeben wird und wenn nicht warum nicht...


Es ist erlaubt das nicht zu mögen ;) aber das nicht freigeben ist nicht das Problem. Der GC ist schlau genug bei Druck auf dem Speicher den Garbage freizugeben hier werden die noch nicht abgearbeiteten Actions aber noch vom Dispatcher referenziert. Da ist also nix freizugeben. Ist eher ein prinzipielles Problem unabhängig ob in managed oder unmanaged Code verbrochen.

Zitat:
Das dachte ich eigentlich auch, aber ich habe in der Schleife und im Delegate jeweils den aktuellen Wert ausgeben lassen. Der stimmt fast überein.


Wenn ich die Ausgabe in Console.WriteLine(x + " " + i) ändere komme ich bis 57072 5706297 da ist dann schon ein Faktor 100 dazwischen und in der Queue stecken somit dann noch mindestens 5706297 - 57072 DispatcherOperation(s).
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4798
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Sa 12.07.14 12:10 
Ups, meinte Dispatcher Queue...

Da ich selber den Dispatcher nur in WPF verwende, habe ich eben an dessen MessageLoop gedacht (aber Dispatcher.Run() entspricht ja dem).
Palladin007 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Sa 12.07.14 12:13 
Stimmt, klingt logisch, dass die Queue überläuft.
Das erklärt auch, warum in der Konsole immer größere Zahlenbereiche übersprungen werden, denn immer, wenn eine Action ausgeführt wird, nimmt er den aktuellen Stand der Schleife und nicht den, der war, als die Action erstellt wurde. Das würde dann wieder anders aus sehen, wenn ich den Parameter x schreiben lasse. Da bekomme ich auch die Exception, weil er schon bei 7,2 Millionen Actions angekommen ist, aber gerade mal ein paar Tausend abarbeiten konnte.
Das erklärt auch, warum das hoch zählen so viel schneller erscheint. Die Schleife an sich zählt einfach vor sich hin, während der Dispatcher sich nach jedem Ausführen den aktuellen Stand der Schleife zum Schreiben nimmt.

Hab mir das mal ausgeben lassen, wie der Unterschied ist und pro geschriebene Zahl werden rund 130 Actions in die Queue gehauen. Es erscheint also so, als würde die Schleife mit 130 facher Geschwindigkeit zählen, wenn ich i schreiben lasse und nicht x, obwohl beide Varianten genau gleich schnell sind.


Joa, ich glaube, ich habs kapiert, danke für die Hilfe :D
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Sa 12.07.14 12:22 
Zitat:
Hab mir das mal ausgeben lassen, wie der Unterschied ist und pro geschriebene Zahl werden rund 130 Actions in die Queue gehauen.


Statistisch auf lange Sicht. Je nachdem wie viel Zeit der DispatcherThread gerade vom System bekommt wirst man es vermutlich auch mal hinbekommen das mehrere Actions vom Dispatcher ausgeführt werden bevor die Schleife über i weiterdreht. Dann solltest man mehrere gleiche Werte auf der Console sehen habe ich aber ohne eingreifen auf die Schnelle nicht hinbekommen.
Palladin007 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Sa 12.07.14 12:31 
Die 130 war der Faktor, der für letzten Millionen Schleifendurchläufe sich ungefähr gehalten hat.
Begonnen hat es ungefähr bei 300, lief dann aber relativ schnell auf 160 zu und blieb dann bei 130.

Gleiche Werte habe ich aber schon gesehen, sogar ziemlich häufig.