Kürzlich habe ich die Implementierung von .NET Hashtable gelesen und bin auf Code gestoßen, den ich nicht verstehe. Ein Teil des Codes ist:
int num3 = 0;
int num4;
do
{
num4 = this.version;
bucket = bucketArray[index];
if (++num3 % 8 == 0)
Thread.Sleep(1);
}
while (this.isWriterInProgress || num4 != this.version);
Der gesamte Code befindet sich innerhalb public virtual object this[object key]
von System.Collections.Hashtable
(mscorlib Version = 4.0.0.0).
Die Frage ist:
Was ist der Grund, Thread.Sleep(1)
dort zu sein?
Antworten:
Sleep (1) ist eine dokumentierte Methode in Windows, um den Prozessor bereitzustellen und die Ausführung anderer Threads zu ermöglichen. Sie finden diesen Code in der Referenzquelle mit Kommentaren:
// Our memory model guarantee if we pick up the change in bucket from another processor, // we will see the 'isWriterProgress' flag to be true or 'version' is changed in the reader. // int spinCount = 0; do { // this is violate read, following memory accesses can not be moved ahead of it. currentversion = version; b = lbuckets[bucketNumber]; // The contention between reader and writer shouldn't happen frequently. // But just in case this will burn CPU, yield the control of CPU if we spinned a few times. // 8 is just a random number I pick. if( (++spinCount) % 8 == 0 ) { Thread.Sleep(1); // 1 means we are yeilding control to all threads, including low-priority ones. } } while ( isWriterInProgress || (currentversion != version) );
Die Variable isWriterInProgress ist ein flüchtiger Bool. Der Autor hatte einige Probleme mit Englisch "Lesen verletzen" ist "flüchtiges Lesen". Die Grundidee ist, das Nachgeben zu vermeiden. Thread-Kontextwechsel sind sehr teuer, mit der Hoffnung, dass der Writer schnell fertig wird. Wenn dies nicht funktioniert, geben Sie explizit nach, um das Verbrennen der CPU zu vermeiden. Dies wäre wahrscheinlich heute mit Spinlock geschrieben worden, aber Hashtable ist sehr alt. Wie sind die Annahmen über das Speichermodell.
quelle
Sleep(0)
wurde empfohlen, wenn Sie nur eine Rendite wollen.Sleep(1)
ist eigentlich ein Schlaf.Ohne Zugriff auf den Rest des Implementierungscodes kann ich nur anhand Ihrer Beiträge eine fundierte Vermutung anstellen.
Das heißt, es sieht so aus, als würde versucht, etwas in der Hashtabelle zu aktualisieren, entweder im Speicher oder auf der Festplatte, und eine Endlosschleife zu machen, während darauf gewartet wird, dass es beendet wird (wie durch Überprüfen der
isWriterInProgress
).Wenn es sich um einen Single-Core-Prozessor handelt, kann jeweils nur ein Thread ausgeführt werden. Eine solche Endlosschleife kann leicht bedeuten, dass der andere Thread keine Chance hat, ausgeführt zu werden, aber dies
Thread.Sleep(1)
gibt dem Prozessor die Möglichkeit, dem Writer Zeit zu geben. Ohne das Warten erhält der Writer-Thread möglicherweise nie die Chance, ausgeführt zu werden, und wird niemals abgeschlossen.quelle
Ich habe die Quelle nicht gelesen, aber es sieht aus wie eine Sache mit sperrenloser Parallelität. Sie versuchen, aus der Hashtabelle zu lesen, aber möglicherweise schreibt eine andere Person darauf. Warten Sie also, bis sie nicht mehr festgelegt
isWriterInProgress
ist und sich die von Ihnen gelesene Version nicht geändert hat.Dies erklärt nicht, warum wir zB immer mindestens einmal warten. EDIT: Das liegt daran, dass wir es nicht tun. Vielen Dank an @Maciej für den Hinweis. Wenn es keinen Streit gibt, fahren wir sofort fort. Ich weiß jedoch nicht, warum 8 die magische Zahl ist, anstatt zB 4 oder 16.
quelle
++num3
,num3
wird 1. Oder fehlt mir ernsthaft Koffein.version
wahrscheinlich die Version der Daten in der Hashtabelle bedeutet (dh jeder Schreibvorgang erstellt eine neue), macht es Sinn :)