SpinWaitSpinWait

System.Threading.SpinWait es un tipo de sincronización ligero que puede usar en escenarios de bajo nivel para evitar los costosos cambios de contexto y las transiciones de kernel que se requieren para los eventos de kernel.System.Threading.SpinWait is a lightweight synchronization type that you can use in low-level scenarios to avoid the expensive context switches and kernel transitions that are required for kernel events. En los equipos con varios núcleos, cuando no se espera que un recurso se mantenga durante largos períodos de tiempo, puede resultar más eficaz que un subproceso en espera itere en modo de usuario en unas docenas o centenas de ciclos y que luego vuelva a intentar adquirir el recurso.On multicore computers, when a resource is not expected to be held for long periods of time, it can be more efficient for a waiting thread to spin in user mode for a few dozen or a few hundred cycles, and then retry to acquire the resource. Si el recurso está disponible después de girar, entonces habrá guardado varios miles de ciclos.If the resource is available after spinning, then you have saved several thousand cycles. Si el recurso aún no está disponible, habrá empleado solo algunos ciclos y podrá activar aún una espera basada en el kernel.If the resource is still not available, then you have spent only a few cycles and can still enter a kernel-based wait. A esta combinación de giro y espera se le denomina a veces una operación de espera de dos fases.This spinning-then-waiting combination is sometimes referred to as a two-phase wait operation.

SpinWait está diseñado para utilizarse junto con los tipos de .NET Framework que contienen eventos de kernel como ManualResetEvent.SpinWait is designed to be used in conjunction with the .NET Framework types that wrap kernel events such as ManualResetEvent. SpinWait también puede utilizarse por sí solo para la funcionalidad de giro básica en un único programa.SpinWait can also be used by itself for basic spinning functionality in just one program.

SpinWait es algo más que un bucle vacío.SpinWait is more than just an empty loop. 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).It is carefully implemented to provide correct spinning behavior for the general case, and will itself initiate context switches if it spins long enough (roughly the length of time required for a kernel transition). 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.For example, on single-core computers, SpinWait yields the time slice of the thread immediately because spinning blocks forward progress on all threads. 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.SpinWait also yields even on multi-core machines to prevent the waiting thread from blocking higher-priority threads or the garbage collector. Por lo tanto, si usa 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.Therefore, if you are using a SpinWait in a two-phase wait operation, we recommend that you invoke the kernel wait before the SpinWait itself initiates a context switch. SpinWait proporciona la propiedad NextSpinWillYield, que puede comprobar antes de cada llamada a SpinOnce.SpinWait provides the NextSpinWillYield property, which you can check before every call to SpinOnce. Cuando se devuelve la propiedad true, inicie su propia operación de espera.When the property returns true, initiate your own Wait operation. Como ejemplo, vea Cómo: Usar SpinWait para implementar una operación de espera de dos fases.For an example, see How to: Use SpinWait to Implement a Two-Phase Wait Operation.

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.If you are not performing a two-phase wait operation but are just spinning until some condition is true, you can enable SpinWait to perform its context switches so that it is a good citizen in the Windows operating system environment. En el siguiente ejemplo básico, se muestra SpinWait en una pila sin bloqueo.The following basic example shows a SpinWait in a lock-free stack. Si necesita una pila segura para subprocesos y de alto rendimiento, considere la posibilidad de usar System.Collections.Concurrent.ConcurrentStack<T>.If you require a high-performance, thread-safe stack, consider using 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

Vea tambiénSee also