Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
System.Threading.SpinWait ist ein einfacher Synchronisierungstyp, den Sie in Szenarien mit niedriger Ebene verwenden können, um die teuren Kontextswitches und Kernelübergänge zu vermeiden, die für Kernelereignisse erforderlich sind. Auf Computern mit mehreren Kernen, wenn eine Ressource nicht über lange Zeiträume gehalten wird, kann es effizienter sein, dass ein Wartenthread für ein paar Dutzend oder ein paar hundert Zyklen im Benutzermodus wartet und dann erneut versucht, die Ressource zu erwerben. Wenn die Ressource nach den Spinvorgängen verfügbar ist, haben Sie mehrere Tausend Zyklen gespeichert. Wenn die Ressource noch nicht verfügbar ist, haben Sie nur einige Zyklen aufgewendet und können trotzdem eine kernelbasierte Wartezeit eingeben. Diese sich drehende und dann wartende Kombination wird manchmal als zweistufiger Wartevorgang bezeichnet.
SpinWait ist dafür konzipiert, in Verbindung mit den .NET-Typen verwendet zu werden, die Kernelereignisse wie zum Beispiel ManualResetEvent umschließen. SpinWait kann auch von sich selbst für grundlegende Spinnfunktionen in nur einem Programm verwendet werden.
SpinWait ist mehr als nur eine leere Schleife. Es ist sorgfältig implementiert, um das richtige Spinverhalten für den Allgemeinfall bereitzustellen, und initiiert selbst Kontextwechsel, wenn es ausreichend lange rotiert hat (ungefähr der für einen Kernelübergang erforderliche Zeitraum). Auf Einzelkerncomputern erzeugt SpinWait z.B. sofort das Zeitsegment des Threads, da sich drehende Blöcke den Status an alle Threads weiterleiten.
SpinWait erzeugt sie ebenfalls, sogar auf Computern mit mehreren Kernen, um zu verhindern, dass der wartende Thread Threads mit höherer Priorität oder den Garbage Collector blockiert. Aus diesem Grund sollten Sie bei Verwendung von SpinWait in einem zweiphasigen Wartevorgang die Kernelwartezeit aufrufen, bevor SpinWait selbst einen Kontextwechsel initiiert.
SpinWait stellt die NextSpinWillYield-Eigenschaft bereit, die Sie vor jedem Aufruf von SpinOnce überprüfen können. Wenn die Eigenschaft true zurückgibt, initiieren Sie Ihren eigenen Wartevorgang. Ein Beispiel finden Sie unter So geht's: SpinWait verwenden, um einen Two-Phase-Wartungsvorgang zu implementieren.
Wenn Sie keinen zweiphasigen Wartevorgang ausführen, sondern einfach Schleifendurchläufe, bis eine Bedingung wahr ist, können Sie SpinWait aktivieren, um dessen Kontextwechsel auszuführen, damit es ein „guter Bürger“ in der Windows-Betriebssystemumgebung ist. Das folgende einfache Beispiel zeigt einen SpinWait in einem sperrfreien Stapel. Wenn Sie einen leistungsstarken, threadsicheren Stack benötigen, sollten Sie die Verwendung von System.Collections.Concurrent.ConcurrentStack<T> in Betracht ziehen.
public class LockFreeStack<T>
{
private volatile Node m_head;
private class Node { public Node Next; public T Value; }
public void Push(T item)
{
var spin = new SpinWait();
Node node = new Node { Value = item }, head;
while (true)
{
head = m_head;
node.Next = head;
if (Interlocked.CompareExchange(ref m_head, node, head) == head) break;
spin.SpinOnce();
}
}
public bool TryPop(out T result)
{
result = default(T);
var spin = new SpinWait();
Node head;
while (true)
{
head = m_head;
if (head == null) return false;
if (Interlocked.CompareExchange(ref m_head, head.Next, head) == head)
{
result = head.Value;
return true;
}
spin.SpinOnce();
}
}
}
Imports System.Threading
Module SpinWaitDemo
Public Class LockFreeStack(Of T)
Private m_head As Node
Private Class Node
Public [Next] As Node
Public Value As T
End Class
Public Sub Push(ByVal item As T)
Dim spin As New SpinWait()
Dim head As Node, node As New Node With {.Value = item}
While True
Thread.MemoryBarrier()
head = m_head
node.Next = head
If Interlocked.CompareExchange(m_head, node, head) Is head Then Exit While
spin.SpinOnce()
End While
End Sub
Public Function TryPop(ByRef result As T) As Boolean
result = CType(Nothing, T)
Dim spin As New SpinWait()
Dim head As Node
While True
Thread.MemoryBarrier()
head = m_head
If head Is Nothing Then Return False
If Interlocked.CompareExchange(m_head, head.Next, head) Is head Then
result = head.Value
Return True
End If
spin.SpinOnce()
End While
End Function
End Class
End Module