SpinWaitSpinWait

System.Threading.SpinWait 是一個輕量型同步處理類型,您可以在低階案例中使用此類型,來避免核心事件所需且成本昂貴的環境切換和核心轉換。System.Threading.SpinWait is a lightweight synchronization type that you can use in low-level scenarios to avoid the expensive context switches and kernel transitions that are required for kernel events. 在多核心電腦上,預期資源不會長時間保留時,若等待中的執行緒要在使用者模式中進行數十個或數百個週期的微調,然後進行重試以取得資源,此類型可能會更有效率。On multicore computers, when a resource is not expected to be held for long periods of time, it can be more efficient for a waiting thread to spin in user mode for a few dozen or a few hundred cycles, and then retry to acquire the resource. 如果資源在微調之後可供使用,則您節省了數千個週期。If the resource is available after spinning, then you have saved several thousand cycles. 如果資源仍然無法使用,則您只花費了數個週期,仍可進入以核心為基礎的等候。If the resource is still not available, then you have spent only a few cycles and can still enter a kernel-based wait. 這個微調然後等候的組合有時稱為「兩階段等候作業」。This spinning-then-waiting combination is sometimes referred to as a two-phase wait operation.

SpinWait 的設計目的是要搭配包裝核心事件(例如)的 .NET 類型使用 ManualResetEventSpinWait is designed to be used in conjunction with the .NET types that wrap kernel events such as ManualResetEvent. SpinWait 也可單獨用來只在一個程式中進行基本微調功能。SpinWait can also be used by itself for basic spinning functionality in just one program.

SpinWait 不只是個空白迴圈。SpinWait is more than just an empty loop. 謹慎地實作它以針對一般案例提供正確的調整行為,而且如果它微調的時間夠長 (大約是核心轉換所需的時間長度),將會自己起始環境切換。It is carefully implemented to provide correct spinning behavior for the general case, and will itself initiate context switches if it spins long enough (roughly the length of time required for a kernel transition). 例如,在單一核心電腦上,SpinWait 會立即產生執行緒的時間配量,因為微調區塊會轉送所有執行緒上的進度。For example, on single-core computers, SpinWait yields the time slice of the thread immediately because spinning blocks forward progress on all threads. 即使在多核心電腦上,SpinWait 也會產生以防止等候中的執行緒封鎖較高優先順序的執行緒或記憶體回收行程。SpinWait also yields even on multi-core machines to prevent the waiting thread from blocking higher-priority threads or the garbage collector. 因此,如果您在兩階段等候作業中使用 SpinWait,我們建議您在 SpinWait 自己起始環境切換之前,先叫用核心等候。Therefore, if you are using a SpinWait in a two-phase wait operation, we recommend that you invoke the kernel wait before the SpinWait itself initiates a context switch. SpinWait 提供 NextSpinWillYield 屬性,您可以在進行每個對 SpinOnce 的呼叫之前檢查此屬性。SpinWait provides the NextSpinWillYield property, which you can check before every call to SpinOnce. 當屬性傳回 true 時,起始您自己的等候作業。When the property returns true, initiate your own Wait operation. 如需範例,請參閱如何:使用 SpinWait 實作兩階段等候作業For an example, see How to: Use SpinWait to Implement a Two-Phase Wait Operation.

如果您並未執行兩階段等候作業,而只是進行微調,直到某些條件成立,則您可以啟用 SpinWait 來執行它的環境切換,讓它成為 Windows 作業系統環境中的好公民。If you are not performing a two-phase wait operation but are just spinning until some condition is true, you can enable SpinWait to perform its context switches so that it is a good citizen in the Windows operating system environment. 下列基本範例示範無鎖定堆疊中的 SpinWaitThe following basic example shows a SpinWait in a lock-free stack. 如果您需要高效能、安全執行緒的堆疊,請考慮使用 System.Collections.Concurrent.ConcurrentStack<T>If you require a high-performance, thread-safe stack, consider using 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

另請參閱See also