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();
}
}
}