Thread-Anzahl begrenzen



  • Hi!

    Kennt jemand eine (plattformunabhängige) Möglichkeit, mit der ich garantieren kann, dass in einer Anwendung zu keiner Zeit mehr als N std::threads laufen? Soll heißen: Wenn man versucht, einen N+1-ten Thread zu starten, soll der Versuch fehlschlagen.

    Hintergrund: Die Tests auf meinem System ergeben, dass es lieber instabil wird/einfriert als bei zu vielen Threads im Konstruktor von std::thread eine Exception zu werfen. Ich möchte auf jeden Fall verhindern, dass das System instabil wird und vorher Maßnahmen ergreifen.



  • Wenn es deine eigene Applikation ist - einfach mitzaehlen und verhindern, oder besser deine Applikation fixen - woher kommt die Instabilität?

    Bei fremder Applikation geht das nicht - wenn eine Applikation einen thread starten will und das nicht geht ist es ein Fehler- oder geht es um thread Pools



  • Versteh ich ehrlich gesagt nicht ganz.

    Ja, std::Thread wirft eine exception wenn die maximale Thread Anzahl per Application überschritten ist aber das sollte eigentlich in einem real Szenario kaum möglich falls du es doch brauchst würde ich zu einem Thread Pool raten.

    Falls Du mit instabil nicht die exception in std::Thread meinst hast du irgendwo ein paar Thread bugs(race condition) die du finden und fixen solltest. Denn eine Reduktion der Threads führt nur zu einer geringeren Wahrscheinlichkeit des Auftretens und sicher wärst Du dann nur wenn Du gar keine extra Threads benutzt.



  • Gast3 schrieb:

    Wenn es deine eigene Applikation ist - einfach mitzaehlen und verhindern, oder besser deine Applikation fixen - woher kommt die Instabilität?

    Mitzählen klingt zwar schön und gut, aber wie? Ich kann den Counter nicht einfach wieder runtersetzen, wenn ein Thread beendet wird, das wäre nicht sicher.

    Gast3 schrieb:

    Bei fremder Applikation geht das nicht - wenn eine Applikation einen thread starten will und das nicht geht ist es ein Fehler- oder geht es um thread Pools

    Ja, ich implementiere an einem Threadpool, aber alles in meiner eigenen Applikation.



  • Ruvi schrieb:

    Ja, std::Thread wirft eine exception wenn die maximale Thread Anzahl per Application überschritten ist aber das sollte eigentlich in einem real Szenario kaum möglich falls du es doch brauchst würde ich zu einem Thread Pool raten.

    Genau einen solchen implementiere ich. Und ich beobachte, dass meine Implementierung von std::thread keine Exceptions wirft, sondern vorher das System instabil wird.

    Ruvi schrieb:

    Falls Du mit instabil nicht die exception in std::Thread meinst hast du irgendwo ein paar Thread bugs(race condition) die du finden und fixen solltest. Denn eine Reduktion der Threads führt nur zu einer geringeren Wahrscheinlichkeit des Auftretens und sicher wärst Du dann nur wenn Du gar keine extra Threads benutzt.

    Nein, ich meine ganz real, dass das System nicht mehr responsive ist und damit meine ich nicht nur meine Anwendung, sondern alle.



  • 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?



  • 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.


Log in to reply