Threadgestaltung bei erwünschtem Abbruch
-
Thread gefunden. www.c-plusplus.net/forum/p2104471#2104471
Überlest einfach mal, was da sonst so steht. Ist ja peinlich.
-
Auch das was in dem Thread sieht für mich OK aus. Tjoah... Threads sind halt so ein Thema was kaum wer wirklich versteht. Auch ich würde mein Leben nicht darauf verwetten dass alles so 100% stimmt wie ich es meine verstanden zu haben. Ich bin mir bloss seeeehr sicher, aber was heisst das schon

Es findet sich sicher auch jmd. der das was in diesem Thread geschrieben wurde für Unfug hält
-
314159265358979 schrieb:
Thread gefunden. www.c-plusplus.net/forum/p2104471#2104471
Überlest einfach mal, was da sonst so steht. Ist ja peinlich.
Die Diskussion zu volatile drehte sich nicht darum, dass volatile hier nicht geht, sondern darum, dass deine Auffassung, was volatile macht, falsch war, dass du dir dadurch Performancezuwachs erhofft hast, ohne wirklich zu messen, was andere (evtl. bessere) Lösungen in dem Fall kostet (pauschal "0,5ms" angeben ist bestenfalls Mutmaßung), und dadurch dein Server unnötig rödelt.
-
Lt Standard, ist volatile ein Hinweis (!) für den Compiler, agressive Optimierung zu vermeiden. Ansonsten soll es sich genau so verhalten, wie es im C-Standard definiert ist. Und dort steht:
- A volatile declaration may be used to describe an object corresponding to a memory-mapped
input/output port or an object accessed by an asynchronously interrupting function. Actions on
objects so declared shall not be ‘‘optimized out’’ by an implementation or reordered except as
permitted by the rules for evaluating expressions.
Ich verstehe das so, dass ein Zugriff auf ein mit volatile spezifiziertem Element nicht vor die vor im stehenden Zugriffe reordered werden darf, und dass das Element im Speicher vorhanden sein muss (es also nicht komplett wegoptimiert werden darf).
Damit ist es nicht atomar (was bei bool aber keinen Unterschied machen sollte).
Wann exakt andere Threads eine Änderung an einem volatile sehen ist damit nicht vorhersagbar, sondern hängt vom Rest des Programms, sowie der verwendeten Maschine ab.
Ist das so in etwas richtig?
- A volatile declaration may be used to describe an object corresponding to a memory-mapped
-
Hi,
danke für die zahlreichen Antworten! Ich habe mir alles durchgelesen und bin nun erfreut, dass mein Verständnis sich verbessert hat. Warum der Zähler über die Leitplanke springt, weiß ich immer noch nicht, denn die eigentliche Zeile ist:
for(std::size_t pos = 0; pos < 2598960; ++pos)
und er dumpt damit, dass die Arraygrenzen bei pos = 2598960 überschritten sind, aber das analysier ich lieber, wenn ich den Rest umgebaut hab.Threads sind halt so ein Thema was kaum wer wirklich versteht.
Oh, danke! Ich dachte, ich würde irgendwo hinterm Mond leben. Ich programmier ja mittlerweile auch ein kleines Weilchen und hatte nie mit Threads zu tun (bedauerlich, ja). Jetzt bastel ich bei dem UI-Zeug damit rum und merke, dass das ein ganz neues Thema ist. Fühle mich wie ein kleines Kind.

Auch mit atomic... Wert wird in Register geschrieben, inkrementiert und zurückgeschrieben... ich hätte jetzt einfach ein rohes bool genommen *schäm*
Edit: Ok, was ja volatile auch reicht. Aber ist diese Definition eigentlich erzwingend? Wenn das nur eine "Recommendation" für den Compiler ist, dann könnte er es ja auch ignorieren. Das jedoch wäre fatal.Irgendwie braucht man für MT echt das Wissen, wie jedes kleine Detail abläuft. Das braucht man sonst oft nicht.
-
volatile schrieb:
Ich verstehe das so, dass ein Zugriff auf ein mit volatile spezifiziertem Element nicht vor die vor im stehenden Zugriffe reordered werden darf, und dass das Element im Speicher vorhanden sein muss (es also nicht komplett wegoptimiert werden darf).
Hängt von der Art des Zugriffes aber, aber im Prinzip: Ja.
volatile schrieb:
Damit ist es nicht atomar (was bei bool aber keinen Unterschied machen sollte).
Natürlich ist es nicht atomar. Was meinst du mit "keinen Unterschied machen"?
volatile schrieb:
Wann exakt andere Threads eine Änderung an einem volatile sehen ist damit nicht vorhersagbar, sondern hängt vom Rest des Programms, sowie der verwendeten Maschine ab.
Das wäre mit Atomics doch auch nicht anders!?
-
Nö ich meinte schon volatile, warum müsste der denn deiner Meinung nach atomic sein?
Hans Böhm empfiehlt bei C++11 auf jeden Fall std::atomic statt volatile zu verwenden.
http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/user-faq.html#orderedUnd auch Anthony Williams implementiert in seinem Buch das interrupt_flag als:
std::atomic<bool> flag;http://www.amazon.com/C-Concurrency-Action-Practical-Multithreading/dp/1933988770
Um Data Races zu vermeiden, sollte std::atomic statt volatile verwendet werden.
-
Es gibt dort aber keine Data Race und ich sehe nicht was ein Atomic dort bringen würde...
-
Hi,
ok, ich habe jetzt Folgendes gebastelt (unwesentliche Teile rausgeschnitten), bin aber total verunsichert:
// Headoa class EquityCalculator { private: struct WorkPackage { WorkPackage() {} // nur für die Berechnung relevante Attribute sowie ctor, der diese füllt }; WorkPackage nextPackage; WorkPackage currentPackage; bool terminateThread; bool newPackageAvailable; void calculate(); public slots: void process(); signals: void calculationFinished(/* results only */); public: EquityCalculator( ); // Stops the last calculation and initiates new one void StartCalculation(/* calculation parameters only */); }; // -------------> Quellcode void EquityCalculator::process() // das ist das RUN vom Thread { while(true) { // if no work package is active and none running while(!newPackageAvailable) { } newPackageAvailable = false; // ZEILE X terminateThread = false; // Now, next package is active currentPackage = nextPackage; calculate(); } } // =================================================== EquityCalculator::EquityCalculator() : terminateThread(false), newPackageAvailable(false) { } // =================================================== void EquityCalculator::StartCalculation( /* calculation parameters only */ ) { terminateThread = true; nextPackage = WorkPackage(/* calculation parameters only */); newPackageAvailable = true; // ZEILE Y } // =================================================== void EquityCalculator::calculate() { try { externalCalculation(/* parameters of current workpackage */, terminateThread); // terminateThread is reference in function; function will test it and throw exception if true emit calculationFinished(/* results */); } catch(const TerminationOccured&) { } }Also die Slots und Signals sind ja nicht unbedingt wichtig, schätze ich. emit schießt halt ein Signal los, sonst gibt es hier keine QT-Details, die ins Gewicht fallen, denke ich.
Also StartCalculation wird von einem anderen Thread ausgeführt. Und jetzt muss halt garantiert sein, dass das nextWorkpackage dort auch gesetzt wird bevor newPackageAvailable auf true gesetzt wird. Hatte das erst anders strukturiert und dann wurde calculate und somit externalCalculation aufgerufen als noch gar nicht alle Felder gesetzt waren.

Jetzt funktioniert es, aber ist es auch thread-safe? Weil, was wenn er gerade in Zeile X ist und dann einer auf StartCalculation drückt, aber der in Zeile Y ist? Jetzt würde eine neue Berechnung ja ignoriert werden. Wie kann ich das lösen?
-
Eisflamme schrieb:
Jetzt funktioniert es, aber ist es auch thread-safe?
Nein ist es nicht und das ist so auch nicht hinzubekommen. Verwend eine Condition Variable wie bereits erklärt.
-
Threads sind halt so ein Thema was kaum wer wirklich versteht.
Threads sind sind keine Quantenphysik. Keine Anhnung, was an Threads so schwer ist zu verstehen.
-
Es gibt dort aber keine Data Race und ich sehe nicht was ein Atomic dort bringen würde...
bool is_abortedrepräsentiert eine Speicherstelle, auf welche mehrere Threads nicht synchronisiert und nicht atomar zugreifen, wobei ein Thread schreibend. Damit liegt ein Data Race vor.
Da es sich hier um nicht-atomare lesende und schreibende Operationen auf diese Variable handelt, verwendest du volatile, was aber meiner Meinung nach nur ein Notbehelf ist.Denn:
If you do use volatile remember that its detailed semantics vary dramatically across platforms. On some platforms, two consecutive volatile stores may become visible out of order to two consecutive volatile loads in another thread. On other platforms, that is explicitly prevented. Thus platform-dependent mechanisms for memory ordering are usually also needed. (The atomic_ops package uses volatile in platform-dependent ways internally, but adds fences to enforce requested memory ordering.)
It is also important to remember that volatile updates are not necessarily atomic. They may appear to be carried out piecemeal. Whether or not they actually are atomic depends on the platform and alignment.
-
einwurf schrieb:
Es gibt dort aber keine Data Race und ich sehe nicht was ein Atomic dort bringen würde...
bool is_abortedrepräsentiert eine Speicherstelle, auf welche mehrere Threads nicht synchronisiert und nicht atomar zugreifen, wobei ein Thread schreibend. Damit liegt ein Data Race vor.
Da es sich hier um nicht-atomare lesende und schreibende Operationen auf diese Variable handelt, verwendest du volatile, was aber meiner Meinung nach nur ein Notbehelf ist.Und wie genau würde ein std::atomic deiner Meinung nach nun dabei helfen? Es gibt keine zwei Threads die jemals gleichzeitig auf den bool schreiben würden. Ich seh also nicht, dass ein Atomic hier irgendwas ändern würde...
EDIT: Ok, es könnte natürlich passieren dass auf irgendeiner äußerst exotischen Architektur beim gleichzeitigen Schreiben im einen und Lesen im anderen Thread Datenmüll gelesen wird. Selbst dann macht das aber wohl nix, wobei es aber vermutlich UB ist. Mit einem atomic wäre dagegen natürlich garantiert dass das auf keinen Fall passiert.
Ich hab mit solchen Dingen meist eher ganz unten wo zu tun, wo es um Performance auf ganz bestimmter Hardware mit ganz bestimmten Compilern und bestimmtem Verhalten geht, da übersieht man sowas schnell mal
Also Ja: Besser std::atomic<bool>, sofern dein Compiler es unterstützt.
-
knivil schrieb:
Threads sind halt so ein Thema was kaum wer wirklich versteht.
Threads sind sind keine Quantenphysik. Keine Anhnung, was an Threads so schwer ist zu verstehen.
Tjoah.
Entweder du überschätzt dich gewaltig, oder du überschätzt den Rest der Programmierer-Gemeinde gewaltig.
Glaub mir, so viel Verwirrung wie beim Thema Threads, vor allem wenn man mal mit Atomics etc. anfängt, gibt es sonst auf kaum einem Gebiet. Zumindest nicht auf einem, mit dem so viele Leute zu tun haben wie mit Threads.Wenn du da wirklich anderer Meinung bist, tendiere ich mal zu der Variante wo du dich überschätzt.
-
sorry,dann habe ich wohl cvs noch nicht kapiert. ich meine,klar. das volatile fehlt hier,aber das allein würde ja trotzdem nix threadsafe machen.
-
Stimmt, Threads sind eigentlich ganz unkompliziert und nicht wirklich schwer zu verstehen.
Das Problem ist die Komplexität der Probleme die man sich mit diesen simplen Threads schaffen kann
-
Eisflamme schrieb:
sorry,dann habe ich wohl cvs noch nicht kapiert. ich meine,klar. das volatile fehlt hier,aber das allein würde ja trotzdem nix threadsafe machen.
Nein, das würde es nicht, dafür ist es ja auch nicht da. Für die Threadsafety wäre die Condition Variable zuständig, die absolut nichts mit dem bool Flag zu tun hat. Wie gesagt, wenn dein Compiler es unterstützt, verwend für das Flag besser einen std::atomic<bool> als volatile.
-
Und wie genau würde ein std::atomic deiner Meinung nach nun dabei helfen?
std::atomic garantiert dass der Compiler die nötige Synchronisation sicherstellt.
Der Compiler weiß, dass der Wert einer Variablen asynchron durch einen anderen Thread verändert werden kann.
Das bedeutet, dass alle Arten spekulativer Compiler-Optimierungen nicht erlaubt sind.
Dazu sind die Operationen (lesen und schreiben) atomar, entweder sie sind durchgeführt oder nicht. Keine halben Sachen.EDIT: Ok, es könnte natürlich passieren dass auf irgendeiner äußerst exotischen Architektur beim gleichzeitigen Schreiben im einen und Lesen im anderen Thread Datenmüll gelesen wird.
Selbst dann macht das aber wohl nix.Das ist undefined behaviour.
-
"Eigentlich ganz unkompliziert" ist mMn. eine relativ wenig sinnvolle Aussage, wenn man deutlich sehen kann, dass so-gut-wie jeder bei einem bestimmten Thema Fehler macht.
-
einwurf schrieb:
Und wie genau würde ein std::atomic deiner Meinung nach nun dabei helfen?
std::atomic garantiert dass der Compiler die nötige Synchronisation sicherstellt.
Der Compiler weiß, dass der Wert einer Variablen asynchron durch einen anderen Thread verändert werden kann.
Das bedeutet, dass alle Arten spekulativer Compiler-Optimierungen nicht erlaubt sind.
Dazu sind die Operationen (lesen und schreiben) atomar, entweder sie sind durchgeführt oder nicht. Keine halben Sachen.EDIT: Ok, es könnte natürlich passieren dass auf irgendeiner äußerst exotischen Architektur beim gleichzeitigen Schreiben im einen und Lesen im anderen Thread Datenmüll gelesen wird.
Selbst dann macht das aber wohl nix.Das ist undefined behaviour.
Ja, wie gleich darauf in meinem Edit geschrieben hast du da völlig recht
