Ich sehe Leute, die das in einigen Teilen von stark multi-threaded, Multi-Prozess-Anwendungssystemen machen, mit denen ich arbeite. Es scheint um Debug-Zeilen herum gemacht zu werden:
%Vor%Wenn ich Schlaf ausschalte (0); (dh ändern Sie es in ""), scheint die Debug-Ausgabe aus dem System in anderer Reihenfolge (weniger vorhersehbar) kommen, so denke ich, es macht die Linie früher kommen - aber ich dachte, std :: cerr wurde ungepuffert und Std: : endl ruft trotzdem std :: flush () auf, also warum sollte das so sein?
Im Grunde gibt es die Kontrolle zurück zum Scheduler, und Sie können sofort neu geplant werden. Das heißt, es ist im Grunde ein Hack, um zu versuchen, das Betriebssystem dazu zu bringen, etwas zu tun.
Und es ist nie eine gute Idee, das Betriebssystem zu täuschen.
Wenn das System angemessen ausgelastet ist, bedeutet das Einschalten des Ruhezustands, dass das Betriebssystem die Kontrolle erhält und die E / A-Warteschlange bündig wird, sodass es diesen Effekt hätte. Manchmal. Abhängig.
Was genau es macht, hängt von Details der Implementierung ab, auf die Sie sich ehrlich nicht verlassen können.
Es stört den Scheduler auf eine schwer vorhersehbare Weise.
Normalerweise wird das Ergebnis einem Aufruf von pthread_yield()
ähnlich sein - Sie geben Ihr Zeitfenster auf. Dies hat zur Folge, dass Sie bei starker Debug-Drucklast weniger wahrscheinlich während des Schreibens auf cerr (dh zwischen diesen <<
s) ausgeschlossen werden, da Sie am Anfang von a stehen Timeslice nach dem letzten Debug-Druck, und so ist es weniger wahrscheinlich, dass mehrere Threads die Ausgabe des anderen überschreiben (dh etwas wie DEBUG: REACHING: foo() @ DEBUG: REACHING bar()17 @ 24
erhalten).
Allerdings ist diese Methode unzuverlässig - sie stört den Scheduler und verlangt keine spezifische Semantik. Es ist auch langsam - bedingungslos in den Kernel einzutreten (und möglicherweise Steuerung zu mehreren Threads zu häufig springen). Und es ist weniger wahrscheinlich, dass es auf einer Multicore-CPU richtig funktioniert.
Es wäre besser, einen Mutex über alle Debug-Ausgaben zu setzen. Da dies jedoch Debug-Code ist, ist es nicht verwunderlich, dass der Autor einen schnellen und schmutzigen Hack verwendet hat, um es gerade gut genug zu bekommen, um jedes Problem zu debuggen.
> Auf den meisten Plattformen bewirkt sleep(0)
, dass der Scheduler den aktuellen Thread mehr oder weniger behandelt, als ob er sein Timeslice aufgebraucht hätte. In der Regel bedeutet dies, dass wenn möglich ein anderer Thread im selben Prozess für diesen Core geplant wird, wenn einer bereit ist. Wie Sie bemerkt haben, neigt es dazu, die Planung etwas berechenbarer zu machen. Das scheint mir beim Debugging kontraproduktiv zu sein.
Ich stimme mit Nemos Kommentar überein, dass es ähnlich wie pthread_yield
ist. Es hat einige legitime Anwendungen, wird aber manchmal fälschlicherweise als eine Möglichkeit verwendet, um mehr Fairness zu erreichen oder die Latenz zu reduzieren. Normalerweise wird die Leistung nur schlechter, weil ein Kontextwechsel die Caches ausbläst.
Ich habe mir die anderen Antworten angeschaut und stimme dem allgemeinen Gefühl der Verwirrung zu. Meine Wette ist, dass es keinen anderen Grund dafür gibt, als dass jemand dachte, dass eine vorhersehbare Planung eine gute Sache sei. (Das ist es nicht. Flexibilität ist gut. Je mehr Anforderungen Sie an den Scheduler stellen, desto mehr muss er opfern, um diese Anforderungen zu erfüllen. Wenn Sie Anforderungen auferlegen, die eigentlich nicht erforderlich sind, tauschen Sie Leistung überhaupt nicht.)
Sie verwenden dies als eine Möglichkeit, andere Prozesse laufen zu lassen, die möglicherweise auf eine bestimmte CPU-Zeit warten, ohne jedoch eine bestimmte Verzögerung zu erzwingen, wenn keine anderen Prozesse darauf warten, ausgeführt zu werden.
Wie andere bereits erwähnt haben, ist sleep(0)
ein Systemaufruf, bei dem der aktuelle Thread die Kontrolle über die CPU abgibt.
Der einzige legitime Grund, dies zu benutzen, ist der, wenn ich ein eigenes Synchronisations-Primitiv wie eine Spin-Sperre mit Assembler-Anweisungen wie LOCK (x86) und; LDREX und STREX (ARMv7). In einem solchen Fall können wir, wenn wir auf eine Sperre stoßen, sofort die Kontrolle abgeben, in der Hoffnung, dass ein anderer Thread seine Sache macht und unsere Sperre aufschließt. Das dreht sich definitiv, bis unsere Zeitscheibe abgelaufen ist.
Nachdem Sie das gesagt haben, müssen Sie wirklich wissen, was Sie tun, wenn Sie Ihr eigenes Synchronisationsprimitiv implementieren. Insbesondere müssen Sie möglicherweise Speicherbarrieren einfügen, um die Reihenfolge von Lese- und Schreibvorgängen zu erzwingen. Sie sind wahrscheinlich besser dran mit den Primitiven auf der Plattform.