Alternativer Timer zu Sleep



  • meine Ansprüche sind eben hoch

    Und dann machst du solche extremen Frickeleien? 😮



  • Marius.. schrieb:

    hustbaer schrieb:

    Ist das jetzt dein Ernst?

    Am Schluss der Threadausführungen erscheit eine kleine Statistik und es spielt eigentlich nur für die Auswertung eine Rolle.
    Und ja es ist mein Ernst, ich kann Leute die aus Bequemlichkeit oder Faulheit heraus, wieder besseren Wissen Dinge falsch machen nicht ausstehen. Wenn ich weiss dass ich mit meiner Arbeit 100% zufrieden bin, dann ist sichergestellt, dass andere es auch sind, meine Ansprüche sind eben hoch.

    dann machs doch einfach richtig. vom user hast du eine variable threadsPerSec und immer, wenn du dran bist, schaust du wieviel zeit vergangen ist und startest so viele threads, wie nötig sind.

    im prinzip so:

    {
       int threads=0;
       time_t start=time(0);
       for(;;)
       {
          Sleep(rnd()%200);
          while(threads<(time()-start)*threadsPerSec)
          {
             startNewThread();
             ++threads;
          }
       }
    }
    

    nur noch ne feinere uhr als time() nehmen und fertig ist der lack.
    das startet dir wenn der user 543 threads pro sekunde haben will auch 543 threads pro sekunde. wenn der rechner mal ruckelt, werden starts einfach nachgeholt, auf lange sicht sinds einfach 543.



  • volkard schrieb:

    Sleep(rnd()%200);
    

    Auch eine gute Idee, dank für die Anregung. Aber für was soll obiger Teil deines Codes gut sein?



  • Marius.. schrieb:

    volkard schrieb:

    Sleep(rnd()%200);
    

    Auch eine gute Idee, dank für die Anregung.

    war nur zur verdeutlichung, daß es auch funktioniert, wenn Sleep(7) recht ungenau sleept, wie es ja manchmal der fall ist.

    Marius.. schrieb:

    Aber für was soll obiger Teil deines Codes gut sein?

    das war der ausgleich, der einfach so viele threads startet, wie nötig sind, um den schnott von threadsPerSec threads per sekunde zu halten.



  • Ist das ne Benchmark oder was?
    Ich zweifle die sinnhaftigkeit eines Parameters welches sich "Threads per Second" nennt allgemein an.
    Für Programme verwendet man normalerweise Thread-Pools o.ä., die eine fixe grösse haben. Und selbst wenn man die grösse des Pools dynamisch anpasst, sehe ich keinen Grund, ein "Threads per Second" Parameter zu haben.



  • Ich kenne die Internetverbindungsgeschwindigkeit der User nicht. Die aufgerufenen Funktionen sammeln Daten aus dem Internet, daher hat der User die Möglichkeit, durch einige Probeläufe festzustellen, welcher Wert für ihn am sinnvollsten ist. Daher war es notwendig diesen Wert variable zu gestalten. Nenn den Wert einfach "Connections per Second", wenn dich "Threads per Second" störrt.
    🙂



  • Hm.
    Ich bezweifle auch dass Connections-per-Second ein sinnvoller Parameter ist.
    Aber selbst wenn, dann würde ich eher nicht pro Connection einen eigenen Thread erstellen, sondern wie erwähnt, einen Thread-Pool verwenden.

    Wie wäre es mit folgenden Parametern:
    * max. open connections
    * max. half-open connections

    Also z.B. es dürfen maximal 500 Connections gleichzeitig offen sein (vollständig verbunden), und maximal 50 Connections gleichzeitig "halb offen" sein (halb offen sind sie während connect() ausgeführt wird).

    Das lässt sich dann ganz ohne Timer, einfach mit ein oder zwei Condition-Variablen und einem Thread-Pool lösen.
    Die nötige Grösse des Pools ist dabei auch leicht zu ermitteln: einfach die max. erlaubte Anzahl an (vollständig) offenen Verbindungen.

    Was hältst du davon?

    p.S.: ein Parameter für "max. half-open connections" ist IMO schonmal deswegen gut, weil Windows XP genau das auf 10 limitiert. Wenn du unter Windows XP über längere Zeit mehr als 10 halb offene Connections hast, dann wirst du irgendwann Timeouts bekommen. Bzw. wenn du versuchst pro Sekunde z.B. 150 Connections zu erstellen, wirst du (bei einer langsamen Verbindung, oder langsamen Servern zu denen du connecten willst) ganz schnell einige hundert "halb offene" Connections haben. Und wenn du pro Connection einen Thread verwendest, dann wird's dich bei 2000 Connections (= 2000 Threads) auf die Schnauze hauen (auf einem 32 Bit System, mit Default Stack-Grösse von 1MB).



  • Jede Connection ist für Sekundenbruchteile offen, dann sind die paar Bytes Daten da und sie wird wieder geschlossen. Ein paar Bytes kann in dem Fall 100 - 10000 bedeuten. Im schlimmsten Fall muss ich so 50000 Connections aufbauen. Wenn zu viel Threads gestartet werden, dann kann der Client nicht die Menge an Daten empfangen die die Server gerade schicken, weil die Internetverbindung des Users der Flaschenhals ist. Starte ich hingegen zu wenige Threads, dann dauert die Abfrage entsprechend lange, daher auch die Frickelei um möglichst genaue Werte einstellen zu können. Die Connections sind übrigens UDP und keine TCP Connections. Wie auch immer es läuft ja soweit zufriedenstellend, vergleichbare Programme sind zumeist langsamer, einige wenige bieten augenscheinlich ähnlich hohe Geschwindigkeiten, daher bin ich mit dem Ergebniss soweit zufrieden.



  • Er.
    Wenn es UDP ist, und du das vernünftig programmierst, brauchst du 1) garkeine Threads und 2) garkeine Parameter um optimale Performance zu bekommen 🙂
    *duck und weg*


  • Mod

    Marius.. schrieb:

    Jede Connection ist für Sekundenbruchteile offen, dann sind die paar Bytes Daten da und sie wird wieder geschlossen. Ein paar Bytes kann in dem Fall 100 - 10000 bedeuten.

    Und was soll diese Anzahl an Threads dann? Das ist eher hinderlich als produktiv...
    Zu dem würde ich mal dem nach gehen was hustbaer in seiner letzten Antwort geschrieben hat 😉



  • Ok, z.Z. siehst es so aus: mein Programm startet einen Thread der nur dafür zuständig ist alle Connection-Threads zu starten.

    Connection Thread sieht dann so aus:
    - starte Anfrage
    - warte auf Antwort (mit Timeoutzeit)
    - werte Antwort aus

    Das hiese jetzt nach euren Vorschlägen ich bräuchte nur noch einen Starter Thread der die Funktionen direkt aufruft. Aber wie sieht es dann mit der Timeoutzeit aus? Da müsste doch zumindest noch irgendwo ein Thread gestartet werden der nur für den Empfang der Daten aller Connections zuständig ist, oder liege ich da falsch? Dummerweise können die Daten auch über mehrere Pakete und in wahloser Reihenfolge eintreffen. Diese Auswertung ist z.Z. jetzt in jedem Thread also geschlossene Funktion realisiert. Ich hab da jetzt ein Verständnissproblem. Vieleicht könnt ihr mir die erforderlichen Ablaufschritte mal aufzeigen.



  • Du verwendest einfach einen einzigen UDP Socket, und zwar non-blocking.

    Jede Connection ist dabei ein Objekt einer selbst geschriebenen Klasse.
    Jede Connection weiss zu jedem Zeitpunkt:
    * ob sie ein Packerl schicken möchte
    * ob sie ein Packerl erwartet
    * bis zu welchem Zeitpunkt max. gewartet werden sollte
    * was beim Timeout passieren soll (z.B. gesendetes Paket neu übertragen oder aufgeben)
    * ...

    In deiner Hauptschleife ermittelst du dann ob irgendeine Connection ein Paket senden möchte. Wenn ja, rufst du select mit "read, write" auf, wenn nein rufst du select bloss mit "read" auf. Timeout für select nimmst du irgendwas kleines, vielleicht 10ms.

    Wenn select zurückkommt machst du folgendes:
    * so oft recvfrom aufrufen bis nixmehr ankommt (vielleicht mit einem Limit von max. 1000 Paketen oder so, falls die CPU langsamer sein sollte als das Netz, was unwahrscheinlich ist, aber theoretisch denkbar)
    * alle Connections checken, ob sie ein Paket senden möchten, und das Paket senden, so lange bis entweder keine Connection mehr senden will, oder sendto EWOULDBLOCK zurückgibt.

    -> zurück zu select()

    void foo()
    {
        forever
        {
            select_mode = read;
            if (any_connection_wants_to_send)
               select_mode = read | write;
    
            select(socket, select_mode, timeout = 10ms);
    
            while (recvfrom(socket, buffer, &sender_address) == OK)
            {
                connection = find_connection(sender_address);
                connection->process_packet(buffer);
            }
    
            for all connections
            {
                connection->tick(); // hier behandelt man z.b. timeouts etc.
    
                if (connection->wants_to_send())
                {
                    buffer = connection->get_send_packet();
                    dest_address = connection->get_peer_address();
                    if (sendto(socket, dest_address) == OK)
                        connection->packet_sent_successfully();
                    else
                        break; // vermutlich EWOULDBLOCK weil sende-puffer voll
                }
            }
        }
    }
    

    Das ist jetzt mal grob, und total unoptimiert.

    Natürlich muss man nicht jede Connection immer "ticken", und nicht jede Connection checken ob sie ein Paket senden möchte etc. Das lässt sich einfach mit diversen Queues lösen, so dass man nur immer die Connections checken muss, die auch was tun wollen.


Anmelden zum Antworten