Partilhar via


SpinWait

System.Threading.SpinWaité um tipo de sincronização leves que podem ser usados em cenários de baixo nível para evitar as alternâncias de contexto caro e transições de kernel que são necessárias para eventos do kernel. Em computadores com vários núcleos, quando um recurso não deve ser mantido por longos períodos de tempo, pode ser mais eficiente para um segmento em espera para alguns ciclos de dezenas de algumas centenas de rotação no modo de usuário e, em seguida, Repetir para adquirir o recurso. Se o recurso está disponível após girando, salvou vários ciclos de milhares. Se o recurso ainda não está disponível, você gastou apenas alguns ciclos e ainda pode inserir uma espera baseado no kernel. Essa combinação de espera de, em seguida, girando às vezes é conhecida como um operação de duas fases esperar.

SpinWaitfoi projetado para ser usado em conjunto com o.NET Framework tipos de eventos do kernel que quebra, como ManualResetEvent. SpinWaittambém pode ser usado sozinho para a funcionalidade básica de rotação em apenas um programa.

SpinWaité mais do que apenas um loop vazio. Seja cuidadosamente implementada para oferecer o comportamento de girando correto para o caso geral e próprio iniciará alternâncias de contexto se ele gira por tempo suficiente (aproximadamente o comprimento de tempo necessário para uma transição de kernel). Por exemplo, em computadores de single-core, SpinWait produz a fatia de tempo do thread imediatamente, pois os blocos girando encaminhar o progresso em todos os threads. SpinWaittambém produz mesmo em máquinas com vários núcleos para impedir que o segmento em espera bloqueando os segmentos de prioridade mais alta ou o coletor de lixo. Portanto, se você estiver usando um SpinWait em uma operação de espera de duas fases, recomendamos que você chame a espera de kernel antes de SpinWait próprio inicia um switch de contexto. SpinWaitfornece a NextSpinWillYield propriedade, que você pode verificar antes de cada chamada para SpinOnce. Quando a propriedade retornará true, iniciar sua própria operação de espera. Para um exemplo, consulte Como: Use SpinWait para implementar uma operação de duas fases de espera.

Se você não estiver executando uma operação de duas fases de espera, mas apenas está girando, até que alguma condição for verdadeira, você pode habilitar SpinWait para executar seus alternâncias de contexto para que ele seja um bom cidadão no ambiente de sistema operacional Windows. A exemplo básico a seguir mostra um SpinWait em uma pilha sem bloqueio. Se você precisar de uma pilha de thread-safe, de alto desempenho, considere o uso de 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();
        }
    }
}

Consulte também

Referência

SpinWait

Outros recursos

Threading objetos e recursos