SpinWait
System.Threading.SpinWait is een lichtgewicht synchronisatietype dat u in scenario's op laag niveau kunt gebruiken om dure contextswitches en kernelovergangen te voorkomen die vereist zijn voor kernelgebeurtenissen. Wanneer op computers met meerdere kernen wordt verwacht dat een resource lange tijd niet wordt bewaard, kan het efficiënter zijn voor een wachtthread die gedurende enkele tientallen of enkele honderd cycli in de gebruikersmodus draait en vervolgens opnieuw probeert om de resource te verkrijgen. Als de resource beschikbaar is na het draaien, hebt u enkele duizenden cycli bespaard. Als de resource nog steeds niet beschikbaar is, hebt u slechts een paar cycli besteed en kunt u nog steeds een wachttijd op basis van een kernel invoeren. Deze draaiende en wachtende combinatie wordt soms aangeduid als een wachtbewerking in twee fasen.
SpinWait is ontworpen om te worden gebruikt in combinatie met de .NET-typen die kernel-gebeurtenissen zoals ManualResetEvent. SpinWait kan ook op zichzelf worden gebruikt voor eenvoudige draaiende functionaliteit in slechts één programma.
SpinWait is meer dan alleen een lege lus. Het wordt zorgvuldig geïmplementeerd om correct draaiend gedrag te bieden voor het algemene geval en zal zelf contextswitches initiëren als deze lang genoeg draait (ongeveer de tijd die nodig is voor een kernelovergang). Op computers SpinWait met één kern levert het tijdssegment van de thread bijvoorbeeld onmiddellijk op omdat het draaien de voortgang van alle threads vooruit blokkeert. SpinWait levert ook zelfs op machines met meerdere kernen op om te voorkomen dat de wachtthread threads met een hogere prioriteit of de garbagecollector blokkeren. Als u daarom een SpinWait in twee fasen wachtende bewerking gebruikt, raden we u aan de kernelwachter aan te roepen voordat de SpinWait zelf een contextswitch start. SpinWait biedt de NextSpinWillYield eigenschap, die u kunt controleren voordat elke aanroep naar SpinOnce. Wanneer de eigenschap wordt geretourneerd true
, start u uw eigen wachtbewerking. Zie Voor een voorbeeld : SpinWait gebruiken om een tweefasenwachtbewerking te implementeren.
Als u geen wachtbewerking met twee fasen uitvoert, maar alleen draait totdat aan een bepaalde voorwaarde wordt voldaan, kunt u de contextswitches inschakelen SpinWait zodat het een goede burger is in de Windows-besturingssysteemomgeving. In het volgende basisvoorbeeld ziet u een SpinWait in een vergrendelingsvrije stack. Als u een krachtige thread-veilige stack nodig hebt, kunt u overwegen om te gebruiken System.Collections.Concurrent.ConcurrentStack<T>.
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