SpinWait
System.Threading.SpinWait 是一種輕量型同步處理型別,可讓您用於低階案例中,避免核心事件所需卻高度耗費資源的內容切換和核心轉換。 在多核心電腦上,當某個資源不應該長期保存時,讓等候中的執行緒在使用者模式中空轉數十個或數百個循環,然後再重試取得資源可能會比較有效率。 如果資源在空轉之後可供使用,您就節省了數千個循環。 如果資源仍然無法使用,您也只花費少數循環,而且仍然可以進入核心架構等候。 這種空轉並等候的組合有時候也稱為「兩階段等候作業」(Two-Phase Wait Operation)。
SpinWait 是設計成搭配包裝核心事件的 .NET Framework 型別使用,例如 ManualResetEvent。 您也可以針對單一程式中的基本空轉功能,單獨使用 SpinWait。
SpinWait 不只是空白的迴圈而已。 您可以仔細地實作此型別,以便提供適用於一般案例的正確空轉行為,而且如果它的空轉時間夠長 (大略等於核心轉換所需的時間長度),就會自行起始內容切換。 例如,在單核心電腦上,SpinWait 會立即產生執行緒的時間量,因為空轉會封鎖所有執行緒的順向進度。 即使在多核心電腦上,SpinWait 也是如此,可避免等候中的執行緒封鎖較高優先權的執行緒或記憶體回收行程。 因此,如果您要在兩階段等候作業中使用 SpinWait,我們建議您在 SpinWait 自行起始內容切換之前叫用核心等候。 SpinWait 會提供 NextSpinWillYield 屬性,讓您在每次呼叫 SpinOnce 之前先行檢查。 當此屬性傳回 true 時,請起始您自己的等候作業。 如需範例,請參閱 HOW TO:使用 SpinWait 實作兩階段等候作業。
如果您不要執行兩階段等候作業,但是只要空轉,直到某個條件成立為止,就可以讓 SpinWait 執行其內容切換,以便成為 Windows 作業系統環境中的好公民。 下列基本範例顯示無鎖定堆疊中的 SpinWait。 如果您需要高效能的安全執行緒堆疊,請考慮使用 System.Collections.Concurrent.ConcurrentStack<T>。
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
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();
}
}
}