System.Threading.SpinWait 是輕量型同步處理類型,您可以在低階案例中使用,以避免核心事件所需的昂貴內容切換和核心轉換。 在多核心計算機上,當不預期資源會被長時間佔用時,讓等候的線程在使用者模式中旋轉幾十個或幾百個週期,然後重試以取得資源,可能會更有效率。 如果資源可在旋轉之後使用,則您已儲存數千個週期。 如果資源仍然無法使用,則您只花費了幾個週期,而且仍然可以輸入以核心為基礎的等候。 這種旋轉等待組合有時稱為 兩階段等候作業。
SpinWait 設計成與包裝核心事件的 .NET 類型搭配使用,例如 ManualResetEvent。 SpinWait 也可以單獨用於一個程式中的基本旋轉功能。
SpinWait 不只是空迴圈。 仔細實作以提供一般案例的正確旋轉行為,而且如果其旋轉時間夠長(大約核心轉換所需的時間長度),則會自行起始內容切換。 例如,在單一核心計算機上,會立即產生線程的時間配量, SpinWait 因為旋轉會封鎖所有線程的向前進度。
SpinWait 也會在多核心計算機上運行,以免等待線程阻塞較高優先順序的線程或垃圾回收器。 因此,如果您在兩階段等候操作中使用 SpinWait ,建議您在 SpinWait 本身進行上下文切換之前叫用核心等待。
SpinWait 提供了 NextSpinWillYield 屬性,您可以在每次呼叫SpinOnce之前先檢查該NextSpinWillYield屬性。 起始您自己的等待操作,當屬性返回true時。 如需範例,請參閱 如何:使用SpinWait實作 Two-Phase 等候作業。
如果您未執行兩階段等待操作,而只是處於某個條件成立之前進行自旋,您可以啟用 SpinWait 進行其上下文切換,以便其在 Windows 作業系統環境中更好地運行。 下列基本範例會在無鎖定堆疊中顯示 SpinWait 。 如果您需要高效能且安全線程的堆疊,請考慮使用 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