다음을 통해 공유


방법: 스핀 잠금에서 스레드-추적 모드 사용

System.Threading.SpinLock은 대기 시간이 매우 짧은 시나리오에서 사용할 수 있는 하위 수준 상호 제외 잠금입니다. SpinLock은 다시 시작할 수 없습니다. 스레드가 이미 획득된 잠금을 다시 잠그려면 먼저 잠금을 올바르게 종료해야 합니다. 일반적으로 이미 획득된 잠금을 다시 잠그려고 하면 디버깅하기 매우 어려울 수 있는 교착 상태가 발생할 수 있습니다. 개발 단계를 위해 System.Threading.SpinLock에서는 스레드가 이미 획득한 잠금을 다시 잠그려고 하면 예외를 throw하는 스레드 추적 모드를 지원합니다. 이 기능을 사용하면 잠금이 올바르게 종료되지 않은 지점을 보다 쉽게 찾을 수 있습니다. SpinLock 생성자를 사용하여 부울 입력 매개 변수를 받아서 true의 인수에 전달하여 스레드 추적 모드를 설정할 수 있습니다. 개발 및 테스트 단계를 완료한 후에는 보다 나은 성능을 위해 스레드 추적 모드를 해제합니다.

예제

다음 예제에서는 스레드 추적 모드를 보여 줍니다. 잠금을 올바르게 종료하는 줄은 다음 결과 중 하나를 생성하는 코딩 오류를 시뮬레이션하도록 주석 처리됩니다.

  • true(Visual Basic의 경우 True)의 인수를 사용하여 SpinLock을 만든 경우 예외가 throw됩니다.

  • false(Visual Basic의 경우 False)의 인수를 사용하여 SpinLock을 만든 경우 교착 상태가 발생합니다.

Imports System.Text
Imports System.Threading
Imports System.Threading.Tasks
Module Module1

    Public Class SpinTest

        ' True means "enable thread tracking." This will cause an
        ' exception to be thrown when the first thread attempts to reenter the lock.
        ' Specify False to cause deadlock due to coding error below.
        Private Shared _spinLock = New SpinLock(True)

        Public Shared Sub Main()

            Parallel.Invoke(
                Sub() DoWork(),
                Sub() DoWork(),
                Sub() DoWork(),
                Sub() DoWork()
                )

            Console.WriteLine("Press any key.")
            Console.ReadKey()
        End Sub

        Public Shared Sub DoWork()

            Dim sb = New StringBuilder()

            For i As Integer = 1 To 9999

                Dim lockTaken As Boolean = False

                Try
                    _spinLock.Enter(lockTaken)

                    ' do work here protected by the lock
                    Thread.SpinWait(50000)
                    sb.Append(Thread.CurrentThread.ManagedThreadId)
                    sb.Append(" Entered-")

                Catch ex As LockRecursionException
                    Console.WriteLine("Thread {0} attempted to reenter the lock",
                                       Thread.CurrentThread.ManagedThreadId)
                    Throw

                Finally

                    ' INTENTIONAL CODING ERROR TO DEMONSTRATE THREAD TRACKING! 
                    ' UNCOMMENT THE LINES FOR CORRECT SPINLOCK BEHAVIOR
                    ' Commenting out these lines causes the same thread
                    ' to attempt to reenter the lock. If the SpinLock was
                    ' created with thread tracking enabled, the exception
                    ' is thrown. Otherwise, if the SpinLock was created with a 
                    ' parameter of false, and these lines are left commented, the spinlock deadlocks.
                    If (lockTaken) Then

                        '  _spinLock.Exit()
                        '  sb.Append("Exited ")
                    End If
                End Try

                ' Output for diagnostic display.
                If (i Mod 4 <> 0) Then
                    Console.Write(sb.ToString())
                Else
                    Console.WriteLine(sb.ToString())
                End If
                sb.Clear()
            Next
        End Sub
    End Class
End Module
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SpinLockDemo
{
    // C#
    public class SpinLockTest
    {
        // Specify true to enable thread tracking. This will cause
        // exception to be thrown when the first thread attempts to reenter the lock.
        // Specify false to cause deadlock due to coding error below.
        private static SpinLock _spinLock = new SpinLock(true);

        static void Main()
        {
            Parallel.Invoke(
                () => DoWork(),
                () => DoWork(),
                () => DoWork(),
                () => DoWork()
                );

            Console.WriteLine("Press any key.");
            Console.ReadKey();
        }

        public static void DoWork()
        {
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < 100; i++)
            {

                bool lockTaken = false;

                try
                {
                    _spinLock.Enter(ref lockTaken);

                    // do work here protected by the lock
                    Thread.SpinWait(50000);
                    sb.Append(Thread.CurrentThread.ManagedThreadId);
                    sb.Append(" Entered-");
                }
                catch (LockRecursionException ex)
                {
                    Console.WriteLine("Thread {0} attempted to reenter the lock",
                                       Thread.CurrentThread.ManagedThreadId);
                    throw;
                }
                finally
                {
                    // INTENTIONAL CODING ERROR TO DEMONSTRATE THREAD TRACKING! 
                    // UNCOMMENT THE LINES FOR CORRECT SPINLOCK BEHAVIOR
                    // Commenting out these lines causes the same thread
                    // to attempt to reenter the lock. If the SpinLock was
                    // created with thread tracking enabled, the exception
                    // is thrown. Otherwise the spinlock deadlocks.
                    if (lockTaken)
                    {
                       // _spinLock.Exit(false);
                       // sb.Append("Exited ");
                    }
                }

                // Output for diagnostic display.
                if(i % 4 != 0)
                    Console.Write(sb.ToString());
                else
                    Console.WriteLine(sb.ToString());
                sb.Clear();

            }
        }
    }
}

참고 항목

개념

SpinLock