SpinWait
System.Threading.SpinWait adalah jenis sinkronisasi ringan yang dapat Anda gunakan dalam skenario tingkat rendah untuk menghindari perubahan konteks yang mahal dan transisi kernel yang diperlukan untuk peristiwa kernel. Pada komputer multicore, ketika sumber daya tidak diharapkan untuk ditahan untuk jangka waktu yang lama, akan lebih efisien bagi alur tunggu untuk berputar dalam mode pengguna selama beberapa lusin hingga beberapa ratus siklus, dan kemudian mencoba lagi untuk memperoleh sumber daya. Jika sumber daya tersedia setelah pemutaran, maka Anda telah menyimpan beberapa ribu siklus. Jika sumber daya masih tidak tersedia, maka Anda hanya menghabiskan beberapa siklus dan masih dapat memasukkan tunggu berbasis kernel. Kombinasi spinning-then-waiting ini terkadang disebut sebagai operasi tunggu dua fase.
SpinWait dirancang untuk digunakan bersama-sama dengan jenis .NET yang membungkus peristiwa kernel seperti ManualResetEvent. SpinWait juga dapat digunakan sendiri untuk fungsionalitas pemutaran dasar hanya dalam satu program.
SpinWait lebih dari sekadar perulangan kosong. Ia diimplementasikan dengan hati-hati untuk memberikan perilaku berputar yang benar untuk kasus umum, dan akan memulai perubahan konteks sendiri jika berputar cukup lama (kira-kira lamanya waktu yang diperlukan untuk transisi kernel). Misalnya, pada komputer berinti tunggal, SpinWait segera menghasilkan potongan waktu alur karena pemutaran memblokir progres berikutnya dari seluruh alur. SpinWait juga menghasilkan bahkan pada komputer berinti ganda utuk mencegah alur tunggu dari memblokir alur prioritas tinggi atau pengumpul sampah. Oleh karena itu, jika Anda menggunakan SpinWait dalam operasi tunggu dua fase, kami sarankan Anda memanggil kernel tunggu sebelum SpinWait memulai perubahan konteks. SpinWait menyediakan properti NextSpinWillYield yang dapat Anda periksa sebelum setiap panggilan ke SpinOnce. Saat properti mengembalikan true
, mulailah operasi Tunggu Anda sendiri. Sebagai contoh, lihat Panduan: Menggunakan SpinWait untuk Menerapkan Operasi Tunggu Dua Fase.
Jika Anda tidak melakukan operasi tunggu dua fase tetapi hanya berputar sampai beberapa kondisi menjadi benar, Anda dapat mengaktifkan SpinWait untuk melakukan perubahan konteksnya sehingga menjadi pengguna yang baik di lingkungan sistem operasi Windows. Contoh dasar berikut menunjukkan SpinWait dalam tumpukan bebas kunci. Jika Anda memerlukan tumpukan berkinerja tinggi dan aman dari alur, pertimbangkan untuk menggunakan 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