SpinWait
System.Threading.SpinWait je jednoduchý typ synchronizace, který můžete použít ve scénářích nízké úrovně, abyste se vyhnuli drahým přepínačům kontextu a přechodům jádra, které jsou vyžadovány pro události jádra. V počítačích s více jádry, pokud se u prostředku neočekává, že se bude uchovávat po dlouhou dobu, může být efektivnější čekat na spuštění vlákna v uživatelském režimu po dobu několika desítek nebo několika stovek cyklů a pak zkuste prostředek získat znovu. Pokud je prostředek po otáčení k dispozici, uložili jste několik tisíc cyklů. Pokud prostředek stále není dostupný, strávili jste pouze několik cyklů a stále můžete zadat čekání založené na jádru. Tato kombinace pro rotující a čekání se někdy označuje jako dvoufázová operace čekání.
SpinWait je navržen pro použití ve spojení s typy .NET, které zabalí události jádra, jako ManualResetEventje . SpinWait lze také použít sám pro základní rotující funkce pouze v jednom programu.
SpinWait je více než jen prázdná smyčka. Je pečlivě implementováno, aby poskytovalo správné rotující chování pro obecný případ, a sám se spustí kontextové přepínače, pokud se roztáčí dostatečně dlouho (zhruba doba potřebná pro přechod jádra). Například na počítačích s jedním jádrem získá časový řez vlákna okamžitě, SpinWait protože otáčení blokuje průběh ve všech vláknech. SpinWait také přináší i na počítačích s více jádry, aby čekací vlákno zabránilo blokování vláken s vyšší prioritou nebo uvolňování paměti. Proto pokud používáte SpinWait v dvoufázové operaci čekání, doporučujeme vyvolat čekání jádra před SpinWait samotným zahájením kontextového přepínače. SpinWaitNextSpinWillYield poskytuje vlastnost, kterou můžete zkontrolovat před každým voláním SpinOnce. Když se tato vlastnost vrátí true
, spusťte vlastní operaci Wait. Příklad: Použití spinwait k implementaci dvoufázové operace čekání.
Pokud neprovádíte dvoufázovou operaci čekání, ale jen se točíte, dokud některá podmínka není pravdivá, můžete povolit SpinWait provádění jeho kontextových přepínačů, aby to byl dobrý občan v prostředí operačního systému Windows. Následující základní příklad ukazuje v zásobníku SpinWait bez uzamčení. Pokud potřebujete vysoce výkonný zásobník bezpečný pro přístup z více vláken, zvažte použití 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