Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
System.Threading.SpinWait est un type de synchronisation léger que vous pouvez utiliser dans des scénarios de bas niveau pour éviter les commutateurs de contexte coûteux et les transitions de noyau requises pour les événements de noyau. Sur les ordinateurs multicœurs, lorsqu’une ressource n’est pas censée être conservée pendant de longues périodes de temps, il peut être plus efficace pour un thread en attente de tourner en mode utilisateur pendant quelques dizaines ou quelques centaines de cycles, puis réessayer d’acquérir la ressource. Si la ressource est disponible après la mise en rotation, vous avez économisé plusieurs milliers de cycles. Si la ressource n’est toujours pas disponible, vous n’avez passé que quelques cycles et pouvez toujours entrer en mode d'attente basé sur le noyau. Cette combinaison de rotation suivie d'attente est parfois appelée opération d'attente en deux phases.
SpinWait est conçu pour être utilisé conjointement avec les types .NET qui encapsulent les événements de noyau tels que ManualResetEvent. SpinWait peut également être utilisé par lui-même pour les fonctionnalités de rotation de base dans un seul programme.
SpinWait est plus qu’une boucle vide. Il est soigneusement implémenté pour fournir un comportement de rotation correct pour le cas général et lancera lui-même des commutateurs de contexte s’il tourne suffisamment longtemps (à peu près la durée nécessaire pour une transition de noyau). Par exemple, sur les ordinateurs à cœur unique, SpinWait interrompt immédiatement la tranche horaire du thread , car la rotation bloque la progression sur tous les threads.
SpinWait cesse même temporairement l’exécution sur les ordinateurs multicœurs pour empêcher le thread en attente de bloquer des threads à priorité plus élevée ou le récupérateur de mémoire. Par conséquent, si vous utilisez un SpinWait dans une opération d’attente en deux phases, nous vous recommandons de lancer l’attente du noyau avant que SpinWait n’initie un changement de contexte.
SpinWait fournit la NextSpinWillYield propriété, que vous pouvez vérifier avant chaque appel à SpinOnce. Lorsque la propriété retourne true
, initialisez votre propre opération d’attente. Pour obtenir un exemple, consultez Guide pratique pour utiliser SpinWait pour implémenter une opération d’attente Two-Phase.
Si vous n’effectuez pas une opération d’attente en deux phases, mais uniquement des rotations jusqu'à ce qu’une condition soit remplie, vous pouvez activer SpinWait pour effectuer des changements de contexte comme il convient dans l’environnement de système d’exploitation Windows. L’exemple de base suivant montre un SpinWait dans une pile sans verrou. Si vous avez besoin d'une pile haute performance et sécurisée pour les threads, envisagez d'utiliser 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