Delen via


SpinWait

System.Threading.SpinWait is een lichtgewicht synchronisatietype dat u in scenario's op laag niveau kunt gebruiken om dure contextswitches en kernelovergangen te voorkomen die vereist zijn voor kernelgebeurtenissen. Wanneer op computers met meerdere kernen wordt verwacht dat een resource lange tijd niet wordt bewaard, kan het efficiënter zijn voor een wachtthread die gedurende enkele tientallen of enkele honderd cycli in de gebruikersmodus draait en vervolgens opnieuw probeert om de resource te verkrijgen. Als de resource beschikbaar is na het draaien, hebt u enkele duizenden cycli bespaard. Als de resource nog steeds niet beschikbaar is, hebt u slechts een paar cycli besteed en kunt u nog steeds een wachttijd op basis van een kernel invoeren. Deze draaiende en wachtende combinatie wordt soms aangeduid als een wachtbewerking in twee fasen.

SpinWait is ontworpen om te worden gebruikt in combinatie met de .NET-typen die kernel-gebeurtenissen zoals ManualResetEvent. SpinWait kan ook op zichzelf worden gebruikt voor eenvoudige draaiende functionaliteit in slechts één programma.

SpinWait is meer dan alleen een lege lus. Het wordt zorgvuldig geïmplementeerd om correct draaiend gedrag te bieden voor het algemene geval en zal zelf contextswitches initiëren als deze lang genoeg draait (ongeveer de tijd die nodig is voor een kernelovergang). Op computers SpinWait met één kern levert het tijdssegment van de thread bijvoorbeeld onmiddellijk op omdat het draaien de voortgang van alle threads vooruit blokkeert. SpinWait levert ook zelfs op machines met meerdere kernen op om te voorkomen dat de wachtthread threads met een hogere prioriteit of de garbagecollector blokkeren. Als u daarom een SpinWait in twee fasen wachtende bewerking gebruikt, raden we u aan de kernelwachter aan te roepen voordat de SpinWait zelf een contextswitch start. SpinWait biedt de NextSpinWillYield eigenschap, die u kunt controleren voordat elke aanroep naar SpinOnce. Wanneer de eigenschap wordt geretourneerd true, start u uw eigen wachtbewerking. Zie Voor een voorbeeld : SpinWait gebruiken om een tweefasenwachtbewerking te implementeren.

Als u geen wachtbewerking met twee fasen uitvoert, maar alleen draait totdat aan een bepaalde voorwaarde wordt voldaan, kunt u de contextswitches inschakelen SpinWait zodat het een goede burger is in de Windows-besturingssysteemomgeving. In het volgende basisvoorbeeld ziet u een SpinWait in een vergrendelingsvrije stack. Als u een krachtige thread-veilige stack nodig hebt, kunt u overwegen om te gebruiken 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

Zie ook