SpinWait

System.Threading.SpinWait es un tipo de sincronización ligero que se puede utilizar en escenarios de bajo nivel para evitar los costosos cambios de contexto y las transiciones del kernel que se requieren para los eventos de kernel. En los equipos de varios núcleos, cuando no se espera que un recurso sea retenido durante períodos de tiempo prolongados, puede ser más eficaz que un subproceso en espera gire en modo usuario durante unas docenas o centenares de ciclos y, a continuación, vuelva a intentar adquirir el recurso. Si el recurso está disponible después de girar, habremos ahorrado varios miles de ciclos. Si el recurso todavía no está disponible, solamente habremos gastado unos cuantos ciclos e igualmente podemos entrar en una espera basada en kernel. Esta combinación de giro y espera se denomina en ocasiones operación de espera de dos fases.

SpinWait se ha diseñado para utilizarlo en combinación con los tipos de .NET Framework que contienen eventos de kernel, como ManualResetEvent. SpinWait también se puede utilizar por sí solo para la funcionalidad del giro básica en un único programa.

SpinWait es más que un bucle vacío sin más. Se ha implementado con todo 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 tiempo necesario para una transición del kernel). Por ejemplo, en los equipos de un núcleo, SpinWait cede de inmediato el intervalo de tiempo del subproceso, porque al girar se bloquea el progreso de avance en todos los subprocesos. SpinWait también cede el paso incluso en equipos de varios núcleos, para evitar que el subproceso en espera bloquee otros subprocesos de más prioridad o el recolector de elementos no utilizados. Por consiguiente, si utiliza SpinWait en una operación de espera de dos fases, recomendamos que invoque la espera del kernel antes de que SpinWait inicie un cambio de contexto. SpinWait proporciona la propiedad NextSpinWillYield, que puede comprobar antes de cada llamada a SpinOnce. Cuando la propiedad devuelve 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 dos fases.

Si no está realizando una operación de espera de dos fases, sino que se limita a girar hasta que se cumpla alguna condición, puede permitir que SpinWait realice sus cambios de contexto para que se comporte correctamente en el entorno del sistema operativo Windows. El siguiente ejemplo básico muestra un objeto SpinWait en una pila sin bloqueo. Si necesita una pila segura para subprocesos de alto rendimiento, considere la posibilidad de usar 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();
        }
    }
}

Vea también

Referencia

SpinWait

Otros recursos

Objetos y características de subprocesos