Bagikan melalui


SpinWait

System.Threading.SpinWait adalah jenis sinkronisasi ringan yang dapat Anda gunakan dalam skenario tingkat rendah untuk menghindari sakelar konteks dan transisi kernel yang mahal yang diperlukan untuk peristiwa kernel. Pada komputer multicore, ketika sumber daya tidak diharapkan ditahan untuk jangka waktu yang lama, mungkin lebih efisien bagi utas tunggu untuk berputar dalam mode pengguna selama beberapa lusin atau beberapa ratus siklus, dan kemudian mencoba lagi untuk memperoleh sumber daya. Jika sumber daya tersedia setelah berputar, maka Anda telah menghemat beberapa ribu siklus. Jika sumber daya masih belum tersedia, maka Anda hanya menghabiskan beberapa siklus dan masih dapat memasuki keadaan tunggu berbasis kernel. Kombinasi berputar-kemudian-menunggu ini kadang-kadang disebut sebagai operasi tunggu dua fase.

SpinWait dirancang untuk digunakan bersama dengan jenis .NET yang membungkus peristiwa kernel seperti ManualResetEvent. SpinWait juga dapat digunakan dengan sendirinya untuk fungsi pemintalan dasar dalam hanya satu program.

SpinWait lebih dari sekadar perulangan kosong. Ini diimplementasikan dengan hati-hati untuk memberikan perilaku berputar yang benar untuk kasus pada umumnya, dan akan memulai sendiri peralihan konteks jika berputar cukup lama (kira-kira waktu yang dibutuhkan untuk transisi kernel). Misalnya, pada komputer inti tunggal, SpinWait memberikan waktu eksekusi pada utas segera karena memutar dapat menghentikan kemajuan pada semua utas. SpinWait juga berfungsi bahkan pada komputer multi-inti untuk mencegah thread tunggu memblokir thread prioritas yang lebih tinggi atau pengumpul sampah. Oleh karena itu, jika Anda menggunakan SpinWait dalam operasi tunggu dua fase, kami sarankan Anda memanggil kernel tunggu sebelum SpinWait itu sendiri memulai pergantian konteks. SpinWait menyediakan properti NextSpinWillYield, yang dapat Anda periksa sebelum setiap panggilan ke SpinOnce. Saat properti mengembalikan true, mulai operasi Tunggu Anda sendiri. Misalnya, lihat Cara: Menggunakan SpinWait untuk Menerapkan Operasi Tunggu Two-Phase.

Jika Anda tidak melakukan operasi tunggu dua fase tetapi hanya berputar sampai beberapa kondisi benar, Anda dapat mengaktifkan SpinWait untuk melakukan sakelar konteksnya sehingga merupakan warga negara yang baik di lingkungan sistem operasi Windows. Contoh dasar berikut menunjukkan sebuah SpinWait yang ada dalam tumpukan bebas kunci. Jika Anda memerlukan tumpukan berkinerja tinggi, dan aman terhadap utas, 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