Share via


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

Lihat juga