Dela via


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 trueinitierar 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

Se även