Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
System.Threading.SpinWait è un tipo di sincronizzazione leggero che è possibile usare in scenari di basso livello per evitare i costosi cambi di contesto e le transizioni del kernel necessarie per gli eventi del kernel. Nei computer multicore, quando non si prevede che una risorsa venga mantenuta per lunghi periodi di tempo, può essere più efficiente per un thread in attesa di ruotare in modalità utente per poche decine o poche centinaia di cicli e quindi riprovare ad acquisire la risorsa. Se la risorsa è disponibile dopo la rotazione, sono state salvate diverse migliaia di cicli. Se la risorsa non è ancora disponibile, hai impiegato solo pochi cicli e puoi comunque entrare in un'attesa basata su kernel. Questa combinazione di rotazione in attesa viene talvolta definita operazione di attesa in due fasi.
SpinWait è progettato per essere usato in combinazione con i tipi .NET che avvolgono eventi del kernel, come ManualResetEvent. SpinWait può anche essere usato da solo per la funzionalità di rotazione di base in un solo programma.
SpinWait è più di un semplice ciclo vuoto. Viene implementato attentamente per fornire un comportamento di rotazione corretto per il caso generale e avvierà le opzioni di contesto se ruota abbastanza a lungo (approssimativamente il periodo di tempo necessario per una transizione del kernel). Ad esempio, nei computer a core singolo, SpinWait restituisce immediatamente la sezione temporale del thread perché la rotazione blocca l'avanzamento in avanti su tutti i thread.
SpinWait cede anche su computer multi-core per impedire al thread in attesa di bloccare i thread con priorità più alta o il raccoglitore di memoria. Pertanto, se si utilizza un SpinWait in un'operazione di attesa a due fasi, è consigliabile richiamare l'attesa del kernel prima che SpinWait stesso avvii un cambio di contesto.
SpinWait fornisce la NextSpinWillYield proprietà , che è possibile controllare prima di ogni chiamata a SpinOnce. Quando la proprietà restituisce true, avvia la tua operazione di attesa. Per un esempio, vedere Procedura: Usare SpinWait per implementare un'operazione di attesa Two-Phase.
Se non si esegue un'operazione di attesa in due fasi ma ci si limita ad attendere in loop fino a quando non viene soddisfatta una condizione, è possibile abilitare SpinWait per eseguire i cambi di contesto, così che funzioni correttamente nell'ambiente del sistema operativo Windows. Nell'esempio di base seguente viene illustrato un oggetto SpinWait in uno stack senza blocchi. Se è necessario uno stack thread-safe ad alte prestazioni, è consigliabile usare 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