멀티쓰레드 프로그래밍

ReaderWriterLock

우대비 2024. 2. 27. 08:06
반응형

ReaderWriterLock은 C#에서 동시성을 관리하는 데 사용되는 동기화 프리미티브입니다.

이 락은 동시에 여러 스레드가 읽기 작업을 수행할 수 있도록 허용하면서,

쓰기 작업을 수행하는 단일 스레드에 대해서는 독점적인 액세스를 제공합니다.

이는 읽기 작업이 빈번하고 쓰기 작업이 상대적으로 드물게 발생하는 시나리오에서 유용합니다.

 

ReaderWriterLock을 사용하면 읽기 작업 중인 데이터에 대한 쓰기 작업을 방지하여

데이터 일관성을 유지할 수 있습니다.

동시에, 여러 스레드가 데이터를 동시에 읽을 수 있어 성능이 향상됩니다.

그러나 쓰기 락이 활성화되면, 해당 락이 해제될 때까지 모든 새로운 읽기 및 쓰기 요청이 대기 상태에 들어갑니다.

 

C# 예시 코드

아래는 ReaderWriterLockSlim을 사용하는 간단한 C# 예제 코드입니다. 

ReaderWriterLockSlim은 .NET에서 제공하는 동기화 프리미티브로, ReaderWriterLock의 간소화된 버전입니다.

이는 여러 스레드가 동시에 읽기 작업을 수행할 수 있게 하면서,

쓰기 작업을 수행하는 단일 스레드에 대해 독점적 액세스를 제공합니다.

ReaderWriterLockSlimReaderWriterLock에 비해 성능이 더 우수하고 사용하기 더 쉬운 API를 제공합니다.

 

ReaderWriterLockSlim은 세 가지 주요 모드를 지원합니다:

  1. 읽기 모드 (Read): 여러 스레드가 동시에 데이터를 읽을 수 있습니다.
  2. 쓰기 모드 (Write): 단 하나의 스레드만 데이터에 쓸 수 있으며, 다른 어떤 스레드도 동시에 읽거나 쓸 수 없습니다.
  3. 업그레이드 가능 읽기 모드 (UpgradableRead): 하나의 스레드가 이 모드를 통해 데이터를 읽으면서 필요에 따라 쓰기 권한으로 업그레이드할 수 있습니다. 이 모드에서는 다른 스레드가 읽기 액세스는 가능하지만, 다른 스레드의 쓰기 액세스는 차단됩니다.

 

using System;
using System.Threading;

public class Example
{
    private static ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
    private static int resource = 0;

    public static void Main()
    {
        // 읽기 스레드 시작
        new Thread(ReadResource).Start();
        new Thread(ReadResource).Start();

        // 쓰기 스레드 시작
        new Thread(WriteResource).Start();
        new Thread(WriteResource).Start();
    }

    // 리소스 읽기
    private static void ReadResource()
    {
        for (int i = 0; i < 10; i++)
        {
            rwLock.EnterReadLock();
            try
            {
                Console.WriteLine($"Resource value read by {Thread.CurrentThread.ManagedThreadId}: {resource}");
                Thread.Sleep(50); // 데이터 읽는 데 걸리는 시간 시뮬레이션
            }
            finally
            {
                rwLock.ExitReadLock();
            }
        }
    }

    // 리소스 쓰기
    private static void WriteResource()
    {
        for (int i = 0; i < 10; i++)
        {
            rwLock.EnterWriteLock();
            try
            {
                resource = new Random().Next(100);
                Console.WriteLine($"Resource value written by {Thread.CurrentThread.ManagedThreadId}: {resource}");
                Thread.Sleep(50); // 데이터 쓰는 데 걸리는 시간 시뮬레이션
            }
            finally
            {
                rwLock.ExitWriteLock();
            }
        }
    }
}

이 예제에서는 두 개의 읽기 스레드와 두 개의 쓰기 스레드를 생성합니다.

각 스레드는 리소스에 대한 액세스를 시도하며,

ReaderWriterLockSlim 인스턴스는 이러한 액세스가 올바르게 동기화되도록 보장합니다.

이렇게 하여 읽기 작업은 동시에 수행될 수 있지만, 쓰기 작업은 한 번에 하나만 수행될 수 있습니다.

 

아래는 간단한 ReaderWriterLock 기능을 수동으로 구현한 C# 코드입니다

using System;
using System.Collections.Generic;
using System.Threading;

public class CustomReaderWriterLock
{
    private int readersCount = 0;
    private bool writerExists = false;
    private object lockObj = new object();

    public void EnterReadLock()
    {
        lock (lockObj)
        {
            // 쓰기 작업이 진행 중일 때까지 대기
            while (writerExists)
            {
                Monitor.Wait(lockObj);
            }
            readersCount++;
        }
    }

    public void ExitReadLock()
    {
        lock (lockObj)
        {
            readersCount--;
            if (readersCount == 0)
            {
                // 모든 읽기 작업이 완료되었으므로 대기 중인 쓰기 작업을 깨움
                Monitor.PulseAll(lockObj);
            }
        }
    }

    public void EnterWriteLock()
    {
        lock (lockObj)
        {
            // 다른 쓰기 작업이 있거나 읽기 작업이 진행 중일 때까지 대기
            while (writerExists || readersCount > 0)
            {
                Monitor.Wait(lockObj);
            }
            writerExists = true;
        }
    }

    public void ExitWriteLock()
    {
        lock (lockObj)
        {
            writerExists = false;
            // 쓰기 작업이 완료되었으므로 대기 중인 읽기 및 쓰기 작업을 깨움
            Monitor.PulseAll(lockObj);
        }
    }
}

public class Program
{
    private static CustomReaderWriterLock rwLock = new CustomReaderWriterLock();
    private static int sharedResource = 0;

    public static void Main()
    {
        Thread writer = new Thread(() =>
        {
            rwLock.EnterWriteLock();
            Console.WriteLine("Writing to shared resource.");
            sharedResource = 42;  // 쓰기 작업
            Thread.Sleep(1000);   // 쓰기 지연 시간
            Console.WriteLine("Finished writing.");
            rwLock.ExitWriteLock();
        });

        Thread reader = new Thread(() =>
        {
            rwLock.EnterReadLock();
            Console.WriteLine($"Reading from shared resource: {sharedResource}");
            Thread.Sleep(500);    // 읽기 지연 시간
            rwLock.ExitReadLock();
        });

        // 스레드 시작
        reader.Start();
        Thread.Sleep(200);  // 쓰기 스레드가 읽기 스레드 이후에 시작하도록 약간 지연
        writer.Start();

        reader.Join();
        writer.Join();
    }
}

이 코드는 CustomReaderWriterLock 클래스를 통해 ReaderWriterLock의 기본 기능을 구현합니다.

EnterReadLock과 ExitReadLock 메서드는 여러 스레드가 동시에 읽기 작업을 수행할 수 있도록 합니다. 반면, EnterWriteLock과 ExitWriteLock 메서드는 단일 스레드가 쓰기 작업을 수행할 때 다른 스레드의 접근을 막습니다.

 

이 예제는 매우 기본적인 형태이며 실제 프로덕션 환경에서는 더 많은 기능과 오류 처리가 필요할 수 있습니다.

그러나 이는 ReaderWriterLock의 핵심 개념과 사용 방법을 이해하는 데 도움이 됩니다.

반응형
LIST

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

Thread Local Storage  (0) 2024.02.27
ManualResetEvent  (0) 2024.02.27
AutoResetEvent  (0) 2024.02.26
Context Switching  (0) 2024.02.26
SpinLock  (0) 2024.02.23