SpinWait
System.Threading.SpinWait är en enkel synkroniseringstyp som du kan använda i scenarier på låg nivå för att undvika dyra kontextväxlar och kernelövergångar som krävs för kernelhändelser. På datorer med flera kärnor, när en resurs inte förväntas hållas under långa tidsperioder, kan det vara mer effektivt för en väntande tråd att snurra i användarläge under några dussin eller några hundra cykler och sedan försöka hämta resursen igen. Om resursen är tillgänglig efter spinning har du sparat flera tusen cykler. Om resursen fortfarande inte är tillgänglig har du bara spenderat några cykler och kan fortfarande ange en kernelbaserad väntan. Den här spinning-then-waiting-kombinationen kallas ibland för en tvåfassvänteåtgärd.
SpinWait är utformad för att användas tillsammans med .NET-typerna som omsluter kernelhändelser som ManualResetEvent. SpinWait kan också användas av sig själv för grundläggande spinning-funktioner i bara ett program.
SpinWait är mer än bara en tom loop. Det implementeras noggrant för att ge rätt snurrande beteende för det allmänna fallet och initierar kontextväxlar om det snurrar tillräckligt länge (ungefär den tid som krävs för en kernelövergång). På datorer SpinWait med en kärna ger till exempel trådens tidssektor omedelbart eftersom snurrande blockerar framåtförloppet för alla trådar. SpinWait ger också även på datorer med flera kärnor för att förhindra att den väntande tråden blockerar trådar med högre prioritet eller skräpinsamlaren. Om du använder en SpinWait i en tvåfass vänteåtgärd rekommenderar vi därför att du anropar kernelvänte innan SpinWait själva initierar en kontextväxel. SpinWait tillhandahåller egenskapen NextSpinWillYield som du kan kontrollera före varje anrop till SpinOnce. När egenskapen returnerar true
initierar du din egen wait-åtgärd. Ett exempel finns i How to: Use SpinWait to Implement a Two-Phase Wait Operation (Använda SpinWait för att implementera en tvåfassvänteåtgärd).
Om du inte utför en tvåfassvänteåtgärd men bara snurrar tills något villkor är sant kan du aktivera SpinWait för att utföra dess kontextväxlar så att det är en bra medborgare i Windows-operativsystemets miljö. I följande grundläggande exempel visas en SpinWait i en låsfri stack. Om du behöver en trådsäker stack med höga prestanda bör du överväga att använda 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