SpinWait

System.Threading.SpinWait는 커널 이벤트에 필요한 비용이 많이 드는 컨텍스트 스위치 및 커널 전환을 방지하기 위해 하위 수준 시나리오에서 사용할 수 있는 단순한 동기화 유형입니다. 멀티 코어 컴퓨터에서, 리소스를 장기간 보유하지 않아도 되는 경우 몇십 번 또는 몇백 번의 주기 동안 대기 스레드가 사용자 모드에서 회전한 다음, 리소스를 획득하려고 시도하는 것이 더 효율적일 수 있습니다. 회전 후에 리소스가 사용 가능해지면 몇천 번의 주기를 절약하는 것입니다. 리소스를 여전히 사용할 수 없는 경우에는 몇 번의 주기만 사용한 것이며 커널 기반의 대기를 계속 입력할 수 있습니다. 이 회전 후 대기 조합을 ‘2단계 대기 작업’이라고도 합니다.

SpinWaitManualResetEvent와 같은 커널 이벤트를 래핑하는 .NET 형식과 함께 사용하도록 설계되었습니다. SpinWait는 한 프로그램의 기본 회전 기능에 대해 자동으로 사용될 수도 있습니다.

SpinWait는 빈 루프보다 큽니다. 일반적인 경우에는 올바른 회전 동작을 제공하도록 신중하게 구현되며, 충분히(대략 커널 전환에 필요한 시간 동안) 회전하는 경우 컨텍스트 스위치를 자동으로 시작합니다. 예를 들어 단일 코어 컴퓨터에서는, 회전 블록이 모든 스레드에서 진행되므로 SpinWait가 스레드의 시간 조각을 즉시 생성합니다. 또한 SpinWait은 대기 스레드가 우선 순위가 높은 스레드 또는 가비지 수집기를 차단하는 것을 방지하기 위해 멀티 코어 컴퓨터에서도 생성합니다. 따라서 2단계 대기 작업에서 SpinWait를 사용하는 경우, SpinWait가 자동으로 컨텍스트 스위치를 시작하기 전에 커널 대기를 호출하는 것이 좋습니다. SpinWaitNextSpinWillYield 속성을 제공합니다. 이 속성은 SpinOnce에 대한 모든 호출 전에 확인할 수 있습니다. 속성에서 true를 반환하면 고유한 대기 작업을 시작하세요. 자세한 내용은 방법: SpinWait을 사용하여 2단계 대기 작업 구현을 참조하세요.

2단계 대기 작업을 수행하지 않지만 일부 조건이 true가 될 때까지 회전하는 경우, Windows 운영 체제 환경에 적합하게 되도록 SpinWait가 자신의 컨텍스트 스위치를 수행하도록 할 수 있습니다. 다음 기본 예제는 잠금 해제 스택의 SpinWait를 보여줍니다. 스레드로부터 안전한 고성능 스택이 필요한 경우 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

참고 항목