C# Game Of Life mit WPF lagt!

!Anonymous

Cadet 4th Year
Registriert
Feb. 2012
Beiträge
92
Hey,
ich habe ein kleines Game Of Life mit der Microsoft Visual C# 2010 Express Edition programmiert. Das ganze läuft per DispatcherTimer und funktioniert bei großen Intervallen auch ganz gut. Wenn das Interval aber kleiner als 200 Millisekunden ist, sieht man, dass der das Programm die Generationen nicht gleichmäßig berechnet und darstellt, denn der Zeitraum zwischen zwei Generationen ist sichtbar unregelmäßig, mal größer, mal kleiner. Ich habe für die Zellen erst Labels genommen und es dann mit Rectangles probiert, ging auch nicht besser.

Wie kann ich es so machen, dass das ganze flüssig und gleichmäßig läuft und welchen Quellcode muss ich posten, damit ihr das beurteilen zu könnt?

MfG
 
Ich rate mal ins Blaue und behaupte, du benutzt den falschen Timer.

Lesen und verstehen ;)

Der DispatcherTimer wird am Anfang jeder Dispatcher-Schleife neu bewertet.

Es kann zwar nicht sichergestellt werden, dass Zeitgeber genau zum Beginn des Zeitintervalls ausgeführt werden, aber dass sie nicht vor dessen Beginn ausgeführt werden, kann sichergestellt werden. Das liegt daran, dass DispatcherTimer-Vorgänge wie andere Vorgänge in der Dispatcher-Warteschlange platziert werden. Wann der DispatcherTimer-Vorgang ausgeführt wird, hängt von den anderen Aufträgen in der Warteschlange und deren Prioritäten ab.

Wenn ein System.Timers.Timer in einer WPF-Anwendung verwendet wird, muss beachtet werden, dass der System.Timers.Timer in einem anderen Thread als der Benutzeroberfläche (user interface, UI)-Thread ausgeführt wird. Für den Zugriff auf Objekte im Benutzeroberfläche (user interface, UI)-Thread muss der Vorgang mit Invoke oder BeginInvoke im Dispatcher des Benutzeroberfläche (user interface, UI)-Threads bereitgestellt werden. Die Gründe für die Verwendung eines DispatcherTimer statt eines System.Timers.Timer liegen darin, dass der DispatcherTimer im gleichen Thread wie der Dispatcher ausgeführt wird und eine DispatcherPriority für den DispatcherTimer festgelegt werden kann.

Ein DispatcherTimer behält ein Objekt bei, wenn die Methoden des Objekts an den Zeitgeber gebunden sind.

Quelle.

Benutz mal einen "regulären" (Nicht-UI-Timer), um die Generationen zu bestimmen. Die UI kannst du dann asynchron mit einem Call zu BeginInvoke des aktuellen Dispatchers updaten - meinetwegen auch mit einer hohen Dispatcher-Priorität.
 
also ich habe jetzt mal den normalen System.Timers.Timer genommen und jetzt sagt er mir, dass er Probleme hat, wegen Zugriff im gleichem Thread und so...
wie kann ich also den Timer in einem eigenen Thread laufen lassen?
 
Der Timer ist ein anderer Thread. Und da du beide Threads nicht synchronisierst (Stichwort. Dispatcher.Invoke - bei WPF) knallts da.

Ich bitte dich nochmal um den Code den du im Tick-Event (oder das Projekt anhängst) hast DANN kann dir geholfen werden.
 
In myClasses.cs:

Code:
public void drawGen( int gen )
{
    for ( int x = 0; x < cols; x++ )
    {
        for ( int y = 0; y < rows; y++ )
        {
            Brush brush = life_status[gen, x, y] ? color_alive : color_dead;

            if ( Application.Current.Dispatcher.CheckAccess() )
            {
                cells[x, y].Background = brush;
            }
            else
            {
                int closureX = x;
                int closureY = y;
                Action setColor = () =>cells[closureX, closureY].Background = brush;
                Application.Current.Dispatcher.BeginInvoke( DispatcherPriority.Normal, setColor );
            }
        }
    }
}

In MainWindow.xaml.cs:
Code:
public void void_gen( object sender, EventArgs e )
{
    ci.setGen( ci.CurrGen );
    ci.CurrGen++;
    ci.drawGen( ci.CurrGen );

    Action setContent = () => lbl_gen.Content = "Generation: " + (ci.CurrGen + 1);
    Dispatcher.CurrentDispatcher.BeginInvoke( DispatcherPriority.Normal, setContent );
}

Edit.
Das ganze kann man noch ein wenig "optimieren", aber wollte dir nicht allzu sehr im Code "rumfuschen".

Edit2.
Um das Kind beim Namen zu nennen.
Der Check, ob man Zugriff auf den Dispatcher-Thread hat, könnte man weglassen, da dieser Fall eh nur eintritt, wenn du manuell durch die Generationen "steppst".
Ebenfalls könnte man überlegen, ob man den Brush wirklich jedem Label neu zuweisen muss.

Edit3.
Hab beim rumspielen mit deinem Code grade noch einen Fehler meinerseits entdeckt :)
Du zählst ja die Generation und zeigst die Aktuelle an. Das klappt mit dem Code, den ich dir oben gegeben habe nicht.

In void void_gen(...) muss es heißen
Code:
lbl_gen.Dispatcher.BeginInvoke( DispatcherPriority.Normal, setContent );
 
Zuletzt bearbeitet:
Meine Lösung wirfte den Timer raus, und du hast nen eigenen Thread um das parallel zu berechnen - damit schaffst auch sehr kleine Zeitintervalle die konstant bleiben.
Nun hast ja 2 Möglichkeiten es zu lösen :)
 

Anhänge

Echt vielen Dank ihr beiden, ihr habt mir echt geholfen :D
U MADE MY DAY!

MfG
Anonymous
 
Ob es bei 2 Möglichkeiten bleibt ist ab zu warten, meistens wenn es um Performance geht kommen gleich mehrere Lösungen die es versuchen immer besser und schneller zu lösen. :)
 
LatinChriz schrieb:
Meine Lösung wirfte den Timer raus, und du hast nen eigenen Thread um das parallel zu berechnen - damit schaffst auch sehr kleine Zeitintervalle die konstant bleiben.
Nun hast ja 2 Möglichkeiten es zu lösen :)

Ich will nicht klugscheißen, aber den eigenen Thread hast du auch mit einem Timer. Der einzige Unterschied zwischen unseren beiden Lösungen ist, dass du Invoke aufrufst und ich BeginInvoke :)
 
Naja hab auch nie gesagt, dass deine Lösung schlecht ist - oder meine besonders gut.
Obwohl es bei dir ordentlich kracht sollte jemand die Anwendung schließen und das Programm noch rechnen ;-).

BeginInvoke funktioniert zwar aber wenn du mal eine kleine Zeitmessung durchführst wirst du auch sehen, dass die Zeitabstände sehr stark schwanken.
 
Zurück
Oben