Autor Beitrag
Kunzi
Hält's aus hier
Beiträge: 2



BeitragVerfasst: Fr 02.04.10 19:05 
Hey Leute, dass ist meine erste Anwendung mit Threads, also bitte nicht all zu hart sein =)
Habe ein kleines Programm geschrieben, dass so oft man will "würfelt", also zufällige Werte zwischen 1 und 6 ausgibt, und den Durchschnitt berechnet.
Lange Geschichte, für was es genau gut ist, aber es beweist, dass sich, je öfter man eben würfelt, der Durchschnitt immer stärker der Zahl 3,5 (Durchschnitt aller würfelbaren Zahlen) nähert. Aber das tut nichts zur Sache.
Da das Programm mit einem Thread bei vielen Durchgängen sehr langsam ist, wollte ich es auf mehrere Threads aufteilen. Herrausgekommen ist das: (in der Rohform, wird natürlich noch besser bearbeitet)
ausblenden volle Höhe 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:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
class Program
    {
        static int x = 0;
        static float ergebnis = 0F;
        static bool anzeigen = false, fertig = false;
        static UInt64 zahl = 0, durchläufe = 0, aufrufe_1 = 0, aufrufe_2 = 0;
        static Random r;

        static void Main(string[] args)
        {
            while (true)
            {
                ThreadStart del = new ThreadStart(rechnen);
                Thread myThread = new Thread(del);

                ergebnis = 0F;
                anzeigen = false;
                fertig = false;
                zahl = 0;
                durchläufe = 0;
                aufrufe_1 = 0;
                aufrufe_2 = 0;

                Console.WriteLine("Startwert eingeben:");
                int seed = Convert.ToInt32(Console.ReadLine());
                r = new Random(seed);
                Console.WriteLine("Anzahl der Durchläufe eingeben:");
                durchläufe = Convert.ToUInt64(Console.ReadLine());
                Console.WriteLine("Gewürfelte Werte anzeigen?");
                if (Console.ReadLine() == "Ja")
                    anzeigen = true;

                myThread.Start();
                for (UInt64 i = 0; i < (durchläufe / 2); i++)
                {
                    x = r.Next(1, 7);
                    zahl += Convert.ToUInt64(x);
                    aufrufe_1 += 1;
                    if(anzeigen)
                        Console.WriteLine("thread 1:  " + x);
                }
                while (fertig != true)
                {
                }
                ergebnis = zahl / (float)durchläufe;
                Console.WriteLine("Durchschnitt: " + Convert.ToString(ergebnis));
                Console.WriteLine("Aufrufe Thread 1: " + aufrufe_1);
                Console.WriteLine("Aufrufe Thread 2: " + aufrufe_2);
            }
        }
        static public void rechnen()
        {
            for (UInt64 i = 0; i < (durchläufe / 2); i++)
            {
                x = r.Next(1, 7);
                zahl += Convert.ToUInt64(x);
                aufrufe_2 += 1;
                if (anzeigen)
                    Console.WriteLine("thread 2:  " + x);
            }
            fertig = true;
        }
    }


Interresant ist, dass der Durchschnitt für "wenige" Durchläufe (in etwa <20000) zu stimmen scheint, aber je mehr Durchläufe, desto eigenartiger wird der Durchschnitt. Dass trifft allerdings nur zu, wenn ich die einzelnen gewürfelten Werte nicht anzeigen lasse. Getestet habe ich es mit einem Startwert (für Zufallsgenerator) von 123 und 100000 Durchläufen. Wenn ich die Werte nicht anzeigen lasse, kommt 1,12... heraus (sollte sich, wie gesagt, 3,5 nähern), lasse ich die Werte allerdings anzeigen, kommt ein Durchschnitt von 3,49... heraus. Mit einem Thread scheint es zu funktionieren, also was mache ich falsch?

Danke schon mal
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 02.04.10 19:24 
Hallo und :welcome:

Du benutzt im Thread und für deine "normalen" Durchläufe die selben globalen Variablen. Das muss krachen, weil du auf beide gleichzeitig zugreifst.

Beispiel:
zahl hat den Wert 100. Jetzt kommt in Thread 1 die Zeile zahl += Convert.ToUInt64(x). Also holt sich der Thread zuerst den Wert von zahl. Also 100. Jetzt kommt Thread 2 mit der selben Zeile dran. Holt sich ebenfalls den Wert, also 100. Dann wird die Zeile in Thread 1 weiter fortgesetzt, es wird x addiert und der neue Wert, z.B. 150, in zahl geschrieben. Dann kommt der zweite Thread dran, schreibt also seinen neuen Wert hinein. Damit ist der erste Wert futsch.

Und solche Effekte gibt es auch noch viele andere, wenn du auf diese Weise mit Threads arbeitest. Deshalb muss der zweite Thread auch eigene Variablen haben und seine Ergebnisse am Ende dem Hauptthread mitteilen. Zudem enthält zahl eine Zahl, die aus beiden Threads gemischt stammt. Dadurch ist dein Durchschnitt auch Zufall, da du ja nicht weißt wie oft der eben genannte Fall aufgetreten ist. Und das verfälscht dann den Durchschnitt.
Kunzi Threadstarter
Hält's aus hier
Beiträge: 2



BeitragVerfasst: Fr 02.04.10 20:29 
Danke für die schnelle Antwort.
Ja, klingt doch irgendwie logisch... aber eben erst nachher.
Funktioniert jetzt super und wird gerade von Grund auf neu überarbeitet =)

Nochmal vielen Dank!