Freigeben über


SpinWait

System.Threading.SpinWait ist ein einfacher Synchronisierungstyp, den Sie in Low-Level-Szenarien verwenden können, um teure Kontextwechsel und Kernelübergänge zu vermeiden, die für Kernelereignisse erforderlich sind. Wenn auf Mehrkerncomputern eine Ressource voraussichtlich nur für kurze Zeit gehalten wird, kann es für einen wartenden Thread effizienter sein, im Benutzermodus für einige Dutzend oder Hundert Zyklen Spinvorgänge durchzuführen und dann erneut zu versuchen, die Ressource abzurufen. Wenn die Ressource nach den Spinvorgängen verfügbar ist, haben Sie mehrere tausend Zyklen gespart. Wenn die Ressource immer noch nicht verfügbar ist, haben Sie nur einige Zyklen durchlaufen und können immer noch in einen kernelbasierten Wartevorgang eintreten. Diese Kombination aus Spin- und Wartevorgängen wird auch als zweiphasiger Wartevorgang bezeichnet.

SpinWait ist für die Verwendung in Verbindung mit den .NET Framework-Typen vorgesehen, die Kernelereignisse wie z. B. ManualResetEvent umschließen. SpinWait kann auch eigenständig für grundlegende Spinfunktionen in nur einem Programm verwendet werden.

SpinWait ist mehr als nur eine leere Schleife. Der Synchronisierungstyp wird sorgfältig implementiert, um richtiges Spinverhalten im Allgemeinen bereitzustellen, und initiiert eigenständig Kontextwechsel, wenn die Spinvorgänge lang genug sind (ungefähr die für einen Kernelübergang erforderliche Zeitspanne). Auf Einzelkerncomputern liefert SpinWait beispielsweise sofort die Zeitscheibe des Threads, da Spinvorgänge den Fortschritt auf allen Threads blockiert. SpinWait wird auch auf Mehrkerncomputern verwendet, um zu verhindern, dass der wartende Thread die Threads mit höherer Priorität oder den Garbage Collector blockiert. Wenn Sie daher SpinWait in einem zweiphasigen Wartevorgang verwenden, empfiehlt es sich, den Kernelwartevorgang aufzurufen, bevor der SpinWait selbst einen Kontextwechsel initiiert. SpinWait stellt die NextSpinWillYield-Eigenschaft bereit, die Sie vor jedem Aufruf von SpinOnce überprüfen können. Wenn die Eigenschaft true zurückgibt, initiieren Sie einen eigenen Wartevorgang. Ein entsprechendes Beispiel finden Sie unter Gewusst wie: Implementieren eines Wartevorgangs mit zwei Phasen mit SpinWait.

Wenn Sie keinen zweiphasigen Wartevorgang initiieren, sondern nur Spinvorgänge durchführen, bis eine Bedingung erfüllt wird, können Sie SpinWait aktivieren, um Kontextwechsel auszuführen und Aufgaben in der Windows-Betriebssystemumgebung zu übernehmen. Das folgende grundlegende Beispiel veranschaulicht SpinWait in einem sperrenfreien Stapel. Wenn Sie einen leistungsstarken, threadsicheren Stapel benötigen, erwägen Sie die Verwendung von 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();
        }
    }
}

Siehe auch

Referenz

SpinWait

Weitere Ressourcen

Threadingobjekte und -features