방법: 스핀 잠금에서 스레드-추적 모드 사용
System.Threading.SpinLock은 대기 시간이 매우 짧은 시나리오에 사용할 수 있는 하위 수준의 상호 배제적인 잠금입니다. SpinLock은 재진입 항목이 아닙니다. 스레드가 잠금 상태가 된 후에는 잠금을 올바르게 종료해야 다시 잠글 수 있습니다. 일반적으로 잠금을 다시 설정하려고 하면 교착 상태가 발생하고, 교착 상태는 디버그하기가 매우 어려울 수 있습니다. 개발에 대한 지원으로 System.Threading.SpinLock은 스레드가 이미 보유하고 있는 잠금을 다시 설정하려고 할 때 예외가 throw되도록 하는 스레드 추적 모드를 지원합니다. 따라서 잠금이 올바르게 종료되지 않은 지점을 쉽게 찾을 수 있습니다. 부울 입력 매개 변수를 사용하고 true
의 인수를 전달하는 SpinLock 생성자를 사용하여 스레드 추적 모드를 켤 수 있습니다. 개발 및 테스트 단계를 완료한 후에는 성능을 향상시키기 위해 스레드 추적 모드를 끄세요.
예시
다음 예제는 스레드 추적 모드를 보여줍니다. 잠금을 올바르게 종료하는 줄은 다음 결과 중 하나를 발생시키는 코딩 오류를 시뮬레이트하기 위해 주석으로 처리됩니다.
SpinLock이
true
(Visual Basic의 경우True
)의 인수를 사용하여 생성된 경우 예외가 throw됩니다.SpinLock이
false
(Visual Basic의 경우False
)의 인수를 사용하여 생성된 경우 교착 상태가 됩니다.
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 {Thread.CurrentThread.ManagedThreadId} attempted to reenter the lock");
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();
}
}
}
}
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
참고 항목
.NET