Compartir a través de


SpinWait

System.Threading.SpinWait es un tipo de sincronización ligero que puede usar en escenarios de bajo nivel para evitar los costosos conmutadores de contexto y transiciones de kernel necesarias para los eventos de kernel. En equipos de varios núcleos, cuando no se espera que un recurso se mantenga durante largos períodos de tiempo, puede ser más eficiente que un hilo en espera dé vueltas en modo de usuario durante unas docenas o unos cientos de ciclos, y luego vuelva a intentar adquirir el recurso. Si el recurso está disponible después de girar, entonces habrá guardado varios miles de ciclos. Si el recurso aún no está disponible, habrá empleado solo algunos ciclos y podrá activar aún una espera basada en el kernel. A esta combinación de giro y espera se le denomina a veces una operación de espera de dos fases.

SpinWait está diseñado para usarse junto con los tipos de .NET que encapsulan eventos de kernel como ManualResetEvent. SpinWait también se puede usar por sí solo para la funcionalidad básica de giro en un solo programa.

SpinWait es más que un bucle vacío. Se implementa con cuidado para proporcionar un comportamiento de giro correcto para el caso general e iniciará por sí mismo cambios de contexto si gira durante el tiempo suficiente (aproximadamente el período de tiempo necesario para una transición del kernel). Por ejemplo, en equipos de un único núcleo, SpinWait da como resultado el intervalo de tiempo del subproceso inmediatamente porque el giro bloquea el progreso en todos los subprocesos. SpinWait también se produce incluso en equipos de varios núcleos para evitar que el subproceso en espera bloquee subprocesos de mayor prioridad o el recolector de elementos no utilizados. Por lo tanto, si usa una SpinWait en una operación de espera en dos fases, se recomienda invocar la espera del kernel antes de que el propio SpinWait inicie un cambio de contexto. SpinWait proporciona la NextSpinWillYield propiedad , que puede comprobar antes de cada llamada a SpinOnce. Cuando se devuelve la propiedad true, inicie su propia operación de espera. Para obtener un ejemplo, vea Cómo usar SpinWait para implementar una operación de espera de Two-Phase.

Si no se está realizando una operación de espera de dos fases, sino que solo gira hasta que una condición sea true, puede habilitar SpinWait para realizar sus cambios de contexto, para actuar correctamente en el entorno del sistema operativo Windows. En el siguiente ejemplo básico se muestra un SpinWait en una pila sin bloqueos. Si necesita una pila segura para subprocesos y de alto rendimiento, considere la posibilidad de usar 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

Consulte también