Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
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