Thread-Anzahl begrenzen



  • Ruvi schrieb:

    Da stimmt etwas nicht.

    Hast Du mal eine blank Applications gebaut mit nix anderem als einer Thread Schleife? Die exception muss kommen, wäre mir jetzt wirklich komplett neu auf Windows habe ich die auf jedenfall schon gesehen.

    Von welchem system reden wir denn?

    Ich habe es schon auf verschiedenen Systemen beobachtet, aktuell z.B. auf Win7 x64. Wenn schon X Threads laufen und die Response-Zeit des Systems merklich zunimmt, ist es für mich schon zu spät. Ich will nicht darauf warten, dass irgendwann vielleicht mal eine Exception kommt oder meine Anwendung ins Messer gelaufen lassen wird, weil die Ressourcen schon nicht mehr reichen, wenigstens eine Exception werfen zu können. Ich möchte einfach von vornherein festlegen, dass höchstens N Threads gleichzeitig existieren.



  • Mir ist nicht bekannt das so etwas existiert. Du bräuchtest das ja dann eigentlich Applikations übergreifend auf OS Ebene.
    Um das zu verhindern gibt es bei Windows das Thread Limit pro Application.

    Mich wundert wirklich, dass dein System schon bei unter 256 zusätzlichen threads in die Knie geht.



  • Ruvi schrieb:

    Mir ist nicht bekannt das so etwas existiert. Du bräuchtest das ja dann eigentlich Applikations übergreifend auf OS Ebene.

    Nein, mir sind andere Applikationen egal, das fällt nicht in meinen Verantwortungsbereich. Ich muss lediglich garantieren, dass meine Anwendung nicht das System instabil werden lässt.

    Ruvi schrieb:

    Um das zu verhindern gibt es bei Windows das Thread Limit pro Application.

    Also kann ich es nur plattformabhängig festlegen. Wo/wie kann ich unter Windows ein Limit setzen? Geht das auch auf BSD und Linux?



  • Jodocus schrieb:

    Nein, mir sind andere Applikationen egal, das fällt nicht in meinen Verantwortungsbereich. Ich muss lediglich garantieren, dass meine Anwendung nicht das System instabil werden lässt.

    DOS lässt grüßen ...

    Jodocus schrieb:

    Also kann ich es nur plattformabhängig festlegen. Wo/wie kann ich unter Windows ein Limit setzen? Geht das auch auf BSD und Linux?

    setrlimit (Stichwort: RLIMIT_NPROC ). Sollte unter Linux/BSDs laufen.
    Für Windows habe ich nix gefunden auf die Schnelle.

    EDIT: Kaputtes Zitat.



  • Jodocus schrieb:

    Ich möchte einfach von vornherein festlegen, dass höchstens N Threads gleichzeitig existieren.

    Ähm, na dann mach halt einfach nie mehr als N Threads!? Ist doch dein Code, wo liegt das Problem!?



  • dot schrieb:

    Jodocus schrieb:

    Ich möchte einfach von vornherein festlegen, dass höchstens N Threads gleichzeitig existieren.

    Ähm, na dann mach halt einfach nie mehr als N Threads!? Ist doch dein Code, wo liegt das Problem!?

    Weil ich nicht weiß, wie ich die Anzahl an aktiven Threads, die mein Programm erstellt hat, ohne Race-Condition zählen kann. Der Thread selbst kann den Counter nicht verringern, denn wenn er es täte, könnte ein anderer Thread starten, während der erste noch nicht beendet ist.



  • http://en.cppreference.com/w/cpp/atomic/atomic/fetch_add 😉

    Viel wichtiger wär aber mal die Frage, wieso deine Anwendung einfach so Threads erzeugt. Das wirkt mir irgendwie äußerst merkwürdig. Insbesonderen wenn es um einen Thread Pool gehen soll. Da erzeugt man normal einmal die nötige Anzahl Threads (z.B. mit in einer Schleife von 1 bis N) und verwendet die dann und fertig...



  • Klar rate condition - aber was denkst du wofuer es Synchronisationsfunktionem/prinzipien gibt - bist du mit thread und nebenlaeufigkeit usw. Fit oder nur am probieren?



  • Jodocus schrieb:

    dot schrieb:

    Jodocus schrieb:

    Ich möchte einfach von vornherein festlegen, dass höchstens N Threads gleichzeitig existieren.

    Ähm, na dann mach halt einfach nie mehr als N Threads!? Ist doch dein Code, wo liegt das Problem!?

    Weil ich nicht weiß, wie ich die Anzahl an aktiven Threads, die mein Programm erstellt hat, ohne Race-Condition zählen kann. Der Thread selbst kann den Counter nicht verringern, denn wenn er es täte, könnte ein anderer Thread starten, während der erste noch nicht beendet ist.

    Dann berücksichtige das bei der Festlegung von N.



  • Du wirst doch sicher eine Art Controller für die Threads haben, oder nicht?.zB die Unit, aus der heraus Du die Threads erzeugst. Dort würde ich den Zähler mitlaufen lassen. Dann noch den Thread beim Beenden dem Controller signalisieren lassen, dass er beendet ist. Unter Windows hab ich das mit Nachrichten gelöst... Der Controller kann dann den Zähler auch wieder verringern. Könntest den Zähler aber auch ganz weglassen und neue Threads nur erzeugen, wenn ein Beendigungssignal von einem der laufenden Threads kommt.

    Disclaimer: Ich bin kein Profi-Programmierer... Aber diese Lösung hat bei mir einwandfrei funktioniert (auch wenn der Hintergrund ein anderer war).



  • dot schrieb:

    Viel wichtiger wär aber mal die Frage, wieso deine Anwendung einfach so Threads erzeugt. Das wirkt mir irgendwie äußerst merkwürdig. Insbesonderen wenn es um einen Thread Pool gehen soll. Da erzeugt man normal einmal die nötige Anzahl Threads (z.B. mit in einer Schleife von 1 bis N) und verwendet die dann und fertig...

    Klar. 😉 Aber wenn ein Burst an Requests in der Aufgabenqueue landet (deren Abarbeitung z.T. zeitaufwändig ist oder auch den Thread blockieren kann, z.B. beim verbinden mit einer Datenbank, man in Datei schreibt oder allgemein eine Ressource lockt), muss ich mehr Threads erzeugen, um die sleeping Threads zu ersetzen, bis sie wieder laufen und der Burst abgearbeitet ist. Es werden also immer dann mehr Threads dazukommen, wenn nicht genug Worker-Threads bereit sind, um Aufgaben aus der Queue abzuarbeiten. So ein Burst darf aber auf keinen Fall dazu führen, dass die Anwendung auf einmal spontan 500 Threads erzeugt und das ganze System lahmlegt, daher eine Obergrenze, am liebsten softwareseitig und plattformunabhängig, aber auf jeden Fall mit Garantie.

    Belli schrieb:

    Dann berücksichtige das bei der Festlegung von N.

    Wie genau meinst du das? Etwa, dass ich sporadisch N lieber etwas kleiner festlege als das reale N?

    Gast3 schrieb:

    Klar rate condition - aber was denkst du wofuer es Synchronisationsfunktionem/prinzipien gibt - bist du mit thread und nebenlaeufigkeit usw. Fit oder nur am probieren?

    Klar bin ich nur am Probieren, aber das heißt nicht, dass ich nicht eine gewisse Ahnung von Multithreading habe. Um meine Expertise geht's hier erst mal auch gar nicht.

    Million Voices schrieb:

    Du wirst doch sicher eine Art Controller für die Threads haben, oder nicht?.zB die Unit, aus der heraus Du die Threads erzeugst. Dort würde ich den Zähler mitlaufen lassen. Dann noch den Thread beim Beenden dem Controller signalisieren lassen, dass er beendet ist.

    Schon klar, das wäre die offensichtliche Lösung, aber sie ist leider falsch:

    if(counter.fetch_add(1, memory_order_relaxed) >= max_threads)
       counter.fetch_sub(1, memory_order_relaxed); // too many threads, don't add
    else thread([&counter]() {
       /* some work ...  */
       counter.fetch_sub(1, memory_order_relaxed); // decrease counter when thread exits
    }).detach();
    

    Der Counter wird verringert, noch während der Thread aktiv ist. Wenn gerade ein Burst abgearbeitet wird und alle Threads nur so in den Startlöchern sitzen, um zusätzliche Threads zu beantragen, rennen sie genau in diese Race Condition und erzeugen mehr Threads als erlaubt.



  • Jodocus schrieb:

    Belli schrieb:

    Dann berücksichtige das bei der Festlegung von N.

    Wie genau meinst du das? Etwa, dass ich sporadisch N lieber etwas kleiner festlege als das reale N?

    Nö. Angenommen, Du willst max. 100 Threads haben. In einem läuft Deine main - Funktion, Du darfst also max. 99 weitere Threads starten. Dafür verwaltest Du einen Counter, ein weiterer Thread wird also nur gestartet, wenn der Counter noch nicht die maximale Anzahl erreicht hat. Jeder Thread, der zu Ende läuft, dekrementiert diesen Counter wieder.
    Dein Argument war ja nun, dass nach dem Dekrement in einem Thread aus main heraus bereits wieder ein Thread gestartet werden könnte, bevor der den Counter dekrementierende Thread tatsächlich auch zu Ende ist (btw. na und? Das ist eine Überschneidung von einem Sekundenbruchteil).
    Um nun also die Gesamtzahl von 100 Threads in keinem Fall zu überschreiten, startet main max. 98 weitere Threads, bzw. genauer, so lange, bis der Counter max. 98 erreicht hat.

    Damit hast Du den von Dir gezeigten Fall abgefangen. Du hast also sozusagen einen Puffer von einem einzigen Thread.

    Was soll überhaupt Dein 'reales N' sein?
    Es geht doch um eine max. Anzahl von Threads, die Deine Anwendung niemals überschreiten soll - das ist dann doch Dein 'reales N'?!



  • Warum nimmst Du keinen Threadpool mit einer festen Anzahl Threads und einer vorgeschalteten Queue, in die die Requests hinein geschrieben werden? Dann blockiert ein lange laufender Request zwar einen Thread im Pool, aber es Du kannst ja (hoffentlich) grob abschätzen, wieviele solcher lang laufender Requests Du erwartest.

    Ansonsten kann ein Thread im Pool, z.B. anhang des Request-Typen, entscheiden, ob er die Anzahl Threads im Pool vergrößert und nach Ende des Requests - wann immer das sein mag - wieder verkleinert. Wäre wahrscheinlich nicht mein Ansatz. Ich nutze meistens eine feste Anzahl Threads im Pool, der meistens doppelt soviele Threads hält, wie der Rechner Kerne hat. (Faustregel)



  • Belli schrieb:

    Damit hast Du den von Dir gezeigten Fall abgefangen.

    Nö. Bloss unwahrscheinlicher gemacht.



  • Sorry, ich versteh nicht was dein Problem mit dem Countrt ist. Du hast ein Limit von Max N. Threads und du erstellst nicht mehr Threads als N.
    Du hast ein Atomic int und jeder Thread der gestartet wird erhöht und jeder Thread der seine Arbeit beendet reduziert den Counter.

    Wie kann da deiner Meinung nach jetzt etwas schief gehen?

    Vielleicht sollte man auch mal das Design in Frage stellen.
    Machen deine vielen Threads überhaupt Sinn?
    Wie ein Vorposter schon richtig gemeint hat machen mehr Threads als Prozessorkerne * 2 nur selten Sinn. Da du dann das Problem mit Thread oversubscription kriegst. Mit anderen Worten dein System ist dann mehr damit beschäftigt von Thread zu Thread zu wechseln statt die eigentliche Arbeit zu erledigen.



  • hustbaer schrieb:

    Belli schrieb:

    Damit hast Du den von Dir gezeigten Fall abgefangen.

    Nö. Bloss unwahrscheinlicher gemacht.

    Hab ich da n Denkfehler drin? Auf welche Weise sollen jetzt mehr als 100 Threads zu einer Zeit entstehen?



  • Jodocus schrieb:

    dot schrieb:

    Jodocus schrieb:

    Ich möchte einfach von vornherein festlegen, dass höchstens N Threads gleichzeitig existieren.

    Ähm, na dann mach halt einfach nie mehr als N Threads!? Ist doch dein Code, wo liegt das Problem!?

    Weil ich nicht weiß, wie ich die Anzahl an aktiven Threads, die mein Programm erstellt hat, ohne Race-Condition zählen kann. Der Thread selbst kann den Counter nicht verringern, denn wenn er es täte, könnte ein anderer Thread starten, während der erste noch nicht beendet ist.

    Wenn das wahr wäre dann könnte es in c++ kein multithreading geben und sowas wie sharedpointer würde auch nicht gehen.
    Dein Counter muss Atomic sein oder mit mutexen gesichert, da sind race Conditions ausgeschlossen.
    Du könntest ein Wrapper um std::Thread bauen und,im constructor increment und,im destruktor decrement machen oder du lässt deinen Thread eine Message an den Main Thread machen die dann die add Thread Funktion vorher abarbeiten muss (mutex).



  • Ruvi schrieb:

    Sorry, ich versteh nicht was dein Problem mit dem Countrt ist. Du hast ein Limit von Max N. Threads und du erstellst nicht mehr Threads als N.
    Du hast ein Atomic int und jeder Thread der gestartet wird erhöht und jeder Thread der seine Arbeit beendet reduziert den Counter.

    Wie kann da deiner Meinung nach jetzt etwas schief gehen?

    Sieh' dir noch mal mein Codebeispiel an und überlege, was passiert, wenn der Thread unterbrochen wird, nachdem er den Counter verringert hat.

    Ruvi schrieb:

    Vielleicht sollte man auch mal das Design in Frage stellen.
    Machen deine vielen Threads überhaupt Sinn?
    Wie ein Vorposter schon richtig gemeint hat machen mehr Threads als Prozessorkerne * 2 nur selten Sinn. Da du dann das Problem mit Thread oversubscription kriegst. Mit anderen Worten dein System ist dann mehr damit beschäftigt von Thread zu Thread zu wechseln statt die eigentliche Arbeit zu erledigen.

    Keine Sorge, über Oversubscription wird sich an anderer Stelle Gedanken gemacht. Hier geht es etwa darum, blockierte Threads (kurzzeitig) zu ersetzen.

    Ruvi schrieb:

    Wenn das wahr wäre dann könnte es in c++ kein multithreading geben und sowas wie sharedpointer würde auch nicht gehen.

    Nein, das hat damit nichts zu tun.

    Ruvi schrieb:

    Dein Counter muss Atomic sein oder mit mutexen gesichert, da sind race Conditions ausgeschlossen.

    Atomic schützt nicht vor Race Conitions, nur vor trivialen Data Races. Mutexes lindern das Problem allerdings, aber weil sie langsam sind und zeitliche Überschneidungen unwahrscheinlicher machen. Aber ein this_thread::sleep_for(1ms) hat den selben Effekt.



  • Einen Wrapper um Thread kann ich nicht bauen, weil ich die Threads detache. Sie laufen völlig autonom und möglichst ohne ständige Synchronisation mit anderen Threads oder geteilten Ressourcen. Das mit der Message hilft afaics nicht gegen die Race Conition.



  • Jodocus schrieb:

    Sieh' dir noch mal mein Codebeispiel an und überlege, was passiert, wenn der Thread unterbrochen wird, nachdem er den Counter verringert hat. Das mit der Message hilft afaics nicht gegen die Race Conition.

    Einer von uns beiden steht gerade gehoerig auf dem Schlauch und ich bin mir nicht sicher wer es ist.

    Wie kann denn bitte der Thread unterbrochen werden nachdem Du den Counter innerhalb des Threads verringert hast? Das sollte doch die letzte Operation/Instruktion sein die der Thread in seiner Lebensspanne vornimmt bevor er beendet wird.

    Ist dein Problem jetzt wirklich ,dass dein Thread nach der letzten Instruktion (counter decrement) eine hypothetische Zeitspanne von x ms noch weiterlebt?
    Das ist eher ein spirituelles, wenn ein richtiges Problem.

    Edit:

    Jodocus schrieb:

    Der Counter wird verringert, noch während der Thread aktiv ist. Wenn gerade ein Burst abgearbeitet wird und alle Threads nur so in den Startlöchern sitzen, um zusätzliche Threads zu beantragen, rennen sie genau in diese Race Condition und erzeugen mehr Threads als erlaubt.

    Das ist meines wissens grundlegend falsch.
    Eine Atomic variable kann sich nur einmal im Speicher eines Prozessors befinden.
    *Genauer: Das Betriebssystem muss sicherstellen, dass sobald eine Instruktion die eine Atomic Variable betrifft ausgefuehrt wird, diese atomic Variable in keinem anderen Prozessor gerade (im selben Moment) benutzt/ausgefuehrt wird.

    Beispiel:
    Wenn das System das nicht sicherstellen wuerde, koennten naemlich folgende Probleme auftreten.

    Du hast Thread A und Thread B und ein atomic<int> mit Wert 0.
    Wenn beide Threads zur exakt gleichen Zeit ein +1 ausfuehren wuerde, haettest Du am Ende einen Wert von 1 statt 2.
    Denn der Prozessor laedt die Variable erhoeht den Wert den er gelesen hat um 1 und schreibt dann die Zahl wieder zurueck, wenn beide Threads das gleichzeitig machen, wuerden beide Threads den Wert 0 lesen ihn +1 rechnen und dann zurueckschreiben.

    Es ist sogar absolut unmoeglich, wenn ich mir deinen Code angucke denn Du checkst deinen Counter mit einer "Add" Konstruktion.
    Mit anderen Worten es ist unmoeglich, dass alle "Burst" Threads die selbe Zahl zur selben Zeit sehen, denn das ist ja der Sinn von atomic Variablen, deswegen versteh ich auch nicht wieso Du meinst das sei eine Race Condition.

    Wenn der counter decrement die letzte Instruktion innerhalb des Threads ist, kann es hoechstens passieren, dass fuer einne "mystischen Akademischen" Moment genau ein einziger Thread mehr existiert als beabsichtigt.


Anmelden zum Antworten