멀티쓰레드 프로그래밍

SpinLock

우대비 2024. 2. 23. 22:22
반응형

SpinLock은 잠금 메커니즘의 한 종류로,

스레드가 잠금을 획득할 수 있을 때까지 계속해서 잠금 상태를 확인하는 방식으로 동작합니다.

이는 "바쁜 대기" 또는 "스핀"이라고 하는 프로세스를 통해 이루어집니다.

다른 잠금 방식과는 달리, 스레드가 잠금을 기다리는 동안 대기 상태로 전환되지 않고,

대신 계속 실행 상태를 유지하며 잠금이 해제될 때까지 루프를 돕니다.

 

SpinLock의 특징:

  1. 성능: SpinLock은 대기 시간이 매우 짧은 상황에서 빠르게 잠금을 획득하고 해제할 수 있으므로, 컨텍스트 스위치의 오버헤드를 줄일 수 있습니다. 하지만 대기 시간이 길어질 경우 CPU 자원을 낭비할 수 있어, 이러한 상황에서는 다른 잠금 메커니즘이 더 적합할 수 있습니다.
  2. 사용 시나리오: SpinLock은 임계 영역(공유 자원에 접근하는 코드 영역)이 짧고, 스레드가 잠금을 빠르게 획득할 것으로 예상되는 경우에 주로 사용됩니다.
  3. 바쁜 대기: SpinLock을 사용할 때 스레드는 잠금을 획득할 수 있을 때까지 계속해서 상태를 확인합니다. 이는 CPU 시간을 소모하지만, 짧은 시간 내에 잠금을 획득할 수 있을 경우에는 이점이 될 수 있습니다.

 

C#에서 SpinLock 사용 예:

using System;
using System.Threading;
using System.Threading.Tasks;

public class SpinLockDemo
{
    private static int sharedResource = 0;
    private static SpinLock spinLock = new SpinLock();

    public static void Main()
    {
        Task[] tasks = new Task[100]; // 여러 작업을 생성합니다.

        for (int i = 0; i < tasks.Length; i++)
        {
            tasks[i] = Task.Run(() =>
            {
                for (int j = 0; j < 1000; j++)
                {
                    bool lockTaken = false;
                    try
                    {
                        // 잠금을 시도하고, 성공할 때까지 계속 시도합니다.
                        spinLock.Enter(ref lockTaken);
                        // 임계 영역: sharedResource 변수를 안전하게 증가시킵니다.
                        sharedResource++;
                    }
                    finally
                    {
                        // 잠금을 성공적으로 획득했다면, 반드시 잠금을 해제해야 합니다.
                        if (lockTaken)
                        {
                            spinLock.Exit();
                        }
                    }
                }
            });
        }

        Task.WaitAll(tasks); // 모든 작업이 완료될 때까지 대기합니다.
        Console.WriteLine($"Final value of sharedResource: {sharedResource}");
    }
}

이 코드는 SpinLock을 사용하여 sharedResource라는 공유 자원에 대한 접근을 동기화하는 방법을 보여줍니다.

각 스레드는 SpinLock.Enter 메서드를 호출하여 잠금을 시도하고,

잠금을 획득하면 공유 자원의 값을 증가시킨 후, SpinLock.Exit 메서드를 호출하여 잠금을 해제합니다.

lockTaken 변수는 잠금 획득 시도의 성공 여부를 나타냅니다.

 

SpinLock 구현

using System;
using System.Threading;

public class SimpleSpinLock
{
    private int isLocked = 0; // 0은 잠금이 해제됨을, 1은 잠금이 설정됨을 나타냅니다.

    public void Enter()
    {
        // 다른 스레드가 잠금을 해제할 때까지 반복적으로 시도합니다.
        while (Interlocked.CompareExchange(ref isLocked, 1, 0) != 0)
        {
            // 여기서 CPU를 적극적으로 사용합니다. 실제 환경에서는 여기에 Thread.Yield()나
            // Thread.Sleep(0)을 추가하여 CPU 사용을 줄일 수 있습니다.
            // 이는 스핀 대기 중에 다른 스레드가 실행될 수 있는 기회를 제공합니다.
        }
    }

    public void Exit()
    {
        // 잠금을 해제합니다.
        Volatile.Write(ref isLocked, 0);
    }
}

 

Interlocked.CompareExchange(ref isLocked, 1, 0)

  1. ref isLocked: 비교 및 교체가 이루어질 변수의 참조입니다. 이 경우, isLocked 변수의 현재 값을 비교하고 조건에 따라 새로운 값으로 교체하려고 합니다.
  2. 1: isLocked 변수의 값이 0과 같을 경우에 할당하고자 하는 새로운 값입니다. 즉, 잠금을 시도하고 있는 스레드는 isLocked를 1로 설정하여 잠금을 획득하려고 합니다.
  3. 0: isLocked 변수의 기대 값입니다. 이 값과 isLocked의 현재 값이 같을 경우에만 isLocked 변수가 새로운 값(여기서는 1)으로 교체됩니다.
반응형
LIST

'멀티쓰레드 프로그래밍' 카테고리의 다른 글

AutoResetEvent  (0) 2024.02.26
Context Switching  (0) 2024.02.26
DeadLock  (1) 2024.02.23
Locking  (0) 2024.02.23
Interlocked  (0) 2024.02.23