Threading.Interlocked.Increment
Verwendung wird ein wenig schneller als eine Sperre zu erwerben, ein Zuwachs zu tun, und Lösen der Verriegelung, aber nicht enorm so. Der teure Teil beider Operationen auf einem Mehrkernsystem erzwingt die Synchronisation von Speichercaches zwischen Kernen. Der Hauptvorteil von Interlocked.Increment
ist nicht Geschwindigkeit, sondern die Tatsache, dass es in einer begrenzten Zeit abgeschlossen wird. Im Gegensatz dazu, wenn jemand versucht, eine Sperre zu erhalten, ein Inkrement auszuführen und die Sperre aufzuheben, besteht auch dann die Gefahr, dass man für einen anderen Thread ewig warten muss, selbst wenn die Sperre zu keinem anderen Zweck als der Überwachung des Zählers verwendet wird erwirbt das Schloss und wird dann weggelegt.
Sie erwähnen nicht, welche Version von .net Sie verwenden, aber es gibt einige Concurrent
Klassen, die möglicherweise von Nutzen sind. Je nachdem, wie man Dinge verteilt und freigibt, ist die Klasse ConcurrentBag
eine Klasse, die ein wenig schwierig erscheinen mag, aber gut funktionieren könnte. Es ist etwas wie eine Warteschlange oder ein Stapel, außer dass es keine Garantie gibt, dass die Dinge in einer bestimmten Reihenfolge kommen. Fügen Sie in Ihrem Ressourcenwrapper ein Flag ein, das angibt, ob es noch gut ist, und fügen Sie der Ressource selbst einen Verweis auf einen Wrapper hinzu. Wenn ein Ressourcenbenutzer erstellt wird, werfen Sie ein Wrapper-Objekt in den Beutel. Wenn der Ressourcenbenutzer nicht mehr benötigt wird, setzen Sie das Flag "ungültig". Die Ressource sollte am Leben bleiben, solange sich mindestens ein Wrapper-Objekt in der Tasche befindet, dessen "gültiges" Flag gesetzt ist, oder die Ressource selbst einen Verweis auf einen gültigen Wrapper enthält. Wenn ein Element gelöscht wird, scheint die Ressource keinen gültigen Wrapper zu enthalten, eine Sperre zu erhalten und, wenn die Ressource immer noch keinen gültigen Wrapper enthält, Wrapper aus dem Beutel zu ziehen, bis ein gültiger Wrapper gefunden wird Speichere das mit der Ressource (oder, falls keine gefunden wurde, zerstöre die Ressource). Wenn ein Element gelöscht wird, enthält die Ressource einen gültigen Wrapper, aber der Beutel scheint so, als könnte er eine übermäßige Anzahl ungültiger Gegenstände enthalten, die Sperre übernehmen, den Inhalt des Beutels in ein Array kopieren und gültige Gegenstände zurück in den Beutel werfen. Behalte die Anzahl der zurückgeworfenen Gegenstände im Auge, so dass man beurteilen kann, wann die nächste Säuberung durchgeführt wird.
sind über viele Eckfällen sorgen Dieser Ansatz kann komplizierter erscheinen als Schlösser oder Threading.Interlocked.Increment
, und es verwenden, aber es kann eine bessere Leistung bieten, weil ConcurrentBag
konzipiert Ressourcenkonflikte zu reduzieren.Wenn der Prozessor 1 an irgendeinem Ort Interlocked.Increment
ausführt, und dann der Prozessor 2, muss der Prozessor 2 den Prozessor 1 anweisen, diesen Ort aus seinem Cache zu löschen, warten, bis der Prozessor 1 dies getan hat, und alle anderen Prozessoren darüber informieren, dass er die Kontrolle benötigt Laden Sie diesen Ort in seinen Cache und kommen Sie schließlich dazu, ihn zu inkrementieren. Nach all dem ist, wenn Prozessor 1 den Standort erneut inkrementieren muss, dieselbe allgemeine Sequenz von Schritten erforderlich. All dies ist sehr langsam. Die ConcurrentBag-Klasse ist dagegen so konzipiert, dass mehrere Prozessoren ohne Cache-Kollision Dinge zu einer Liste hinzufügen können. Irgendwann, wenn Dinge hinzugefügt und wenn sie entfernt werden, müssen sie in eine kohärente Datenstruktur kopiert werden, aber solche Operationen können in Stapeln so ausgeführt werden, dass eine gute Cache-Leistung erzielt wird.
Ich habe einen Ansatz wie oben unter Verwendung ConcurrentBag
nicht versucht, so weiß ich nicht, welche Art von Leistung es tatsächlich ergeben würde, aber je nach Nutzungsmuster kann es möglich sein, bessere Leistung zu geben, als erhalten würde über Referenzzählung.
Sind 'Interlocked.Increment' (und verwandte) was Sie suchen? – harold
Welche Version des .Net-Frameworks verwenden Sie? – pstrjds
Meinen Sie, dass die Initialisierung und das Herunterfahren zwischen * jeder * Operation oder kurz vor der ersten Operation und nach der letzten ausgeführt werden müssen? – jalf