다음을 통해 공유


SpinWait

System.Threading.SpinWait는 하위 수준 시나리오에서 커널 이벤트에 필요한 부담이 큰 컨텍스트 전환 및 커널 전환을 대신하기 위해 사용할 수 있는 간단한 동기화 형식입니다. 다중 코어 컴퓨터에서 리소스가 오랫동안 보관될 것으로 예상되지 않는 경우 사용자 모드에서 대기 중인 스레드를 수십 또는 수백 차례 더 회전한 다음 리소스 확보를 다시 시도하는 것이 더 효율적일 수 있습니다. 회전 후 리소스를 사용할 수 있으면 수천 차례 저장한 것입니다. 아직 리소스를 사용할 수 없으면 하나의 사이클만 사용되었고 여전히 커널 기반 대기에 들어갈 수 있습니다. 이 회전 후 대기 조합은 2단계 대기 작업이라고도 합니다.

SpinWaitManualResetEvent와 같은 커널 이벤트를 래핑하는 .NET Framework 형식과 함께 사용되도록 설계되었습니다. 또한 SpinWait는 기본 회전 기능을 제공하기 위해 하나의 프로그램에서 단독으로 사용할 수도 있습니다.

SpinWait는 빈 루프 이상의 기능을 수행합니다. 즉, 일반적인 경우에는 올바른 회전 동작을 제공할 수 있도록 신중하게 구현되어 있으며, 충분히 회전한 경우(즉, 커널 전환에 필요한 시간 만큼 회전한 경우)에는 컨텍스트 전환을 시작합니다. 예를 들어 단일 코어 컴퓨터에서는 모든 스레드에서 회전 블록이 앞으로 진행되기 때문에 SpinWait가 스레드의 시간 간격을 즉시 계산합니다. 또한 SpinWait는 다중 코어 컴퓨터에서도 대기 스레드가 우선 순위가 더 높은 스레드나 가비지 수집기를 차단하는 것을 방지하기 위해 시간 간격을 계산합니다. 따라서 2단계 대기 작업에서 SpinWait를 사용하는 경우에는 SpinWait가 컨텍스트 전환을 시작하기 전에 커널 대기를 호출하는 것이 좋습니다. SpinWaitSpinOnce가 호출될 때마다 그전에 확인할 수 있는 NextSpinWillYield 속성을 제공합니다. 이 속성이 true를 반환하면 사용자 고유의 대기 작업을 시작합니다. 예제를 보려면 방법: SpinWait을 사용하여 2단계 대기 작업 구현을 참조하십시오.

2단계 대기 작업을 수행하지 않고 특정 조건이 true가 될 때까지 단순히 회전하고 있는 경우 SpinWait가 컨텍스트 전환을 수행하여 Windows 운영 체제 환경에서 제대로 작동하도록 설정할 수 있습니다. 다음 기본 예제에서는 잠금이 필요 없는 스택에 있는 SpinWait를 보여 줍니다. 스레드로부터 안전한 고성능 스택이 필요하면 System.Collections.Concurrent.ConcurrentStack<T>을 사용하는 것이 좋습니다.

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
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();
        }
    }
}

참고 항목

참조

SpinWait

기타 리소스

스레딩 개체 및 기능