스레드 동기화(Thread Synchronization)
데이터 불일치는 두 개 이상의 스레드가 메모리 내부의 데이터를 동시에 접근할 때 발생합니다. 예를 들어 두 개의 스레드가 abcd.txt라는 텍스트 파일을 접근한다고 가정합니다. 스레드1은 abcd.txt 파일에 데이터를 작성(Write)하고 있으며, 스레드2는 abcd.txt 파일의 데이터를 읽는(Read) 중입니다. 작성되고 있는 데이터가 읽는 중에 반영되지 않으므로 데이터 불일치가 발생합니다.
스레드 동기화는 위 예시에서 언급한 abcd.txt 파일이라던지 메모리 내부의 데이터를 특정 스레드가 사용하고 있으면 다른 스레드는 접근할 수 없도록 하는 메커니즘입니다. 스레드1이 abcd.txt 파일에 데이터를 작성하고 있으면, 스레드2는 스레드1의 작업이 완료될 때까지 기다렸다가 스레드1의 작업이 완료되면 abcd.txt 파일의 데이터를 읽는 것이죠.
잠금(Lock)
특정 스레드가 abcd.txt 파일의 데이터를 사용하고 있는 경우 다른 스레드는 abcd.txt 파일의 데이터를 접근할 수 없어야 합니다. 잠금(Lock)이라는 메커니즘을 사용하여 특정 스레드가 공유 리소스를 사용 중인 경우 다른 스레드가 공유 리소스에 접근할 수 없도록 할 수 있습니다.
잠금에는 배타적 잠금(Exclusive Lock)과 비배타적 잠금(Non-Exclusive Lock)이 존재합니다.
배타적 잠금(Exclusive Lock)
배타적 잠금은 공유 리소스를 오직 하나의 스레드만 접근할 수 있습니다. C#에서 지원하는 lock 키워드, Monitor, Mutex, SpinLock 클래스를 사용하여 배타적 잠금을 구현할 수 있습니다.
비배타적 잠금(Non-Exclusive Lock)
비배타적 잠금은 공유 리소스를 접근할 수 있는 스레드의 개수를 제한합니다. C#에서 지원하는 Semaphore, SemphoreSlim, ReaderWriterLockSlim 클래스를 사용하여 비배타적 잠금을 구현할 수 있습니다.
동기화 방법
C#에서 스레드 동기화는 여러 가지 방법으로 구현할 수 있습니다. 이번 포스팅에서 사용하는 방법은 lock 키워드와 잠금을 설정해야하는 객체를 사용하는 것입니다.
lock (lockObject)
{
// 로직 작성...
}
공유 리소스를 처리하는 로직을 lock 키워드의 코드 블록에 작성합니다. 특정 스레드 객체가 lock 키워드의 코드 블록을 실행하고 있으면, 다른 스레드 객체는 lock 키워드의 코드 블록에 접근할 수 없습니다. lock 키워드의 코드 블록이 끝날 때까지 기다려야 합니다.
동기화 없는 스레드 예제
동기화 예제를 확인하기 전에 동기화를 사용하지 않았을 때 어떤 일이 발생하는지 살펴봅시다.
다음 예제는 스레드 객체가 동일한 리소스를 접근합니다. 동일한 리소스는 PrintName() 메서드입니다. 동기화가 없으므로 PrintName() 메서드는 특정 시간에 모든 스레드 객체가 접근할 수 있습니다.
class Program
{
public static void PrintName(Object name)
{
Console.Write("My Name is ");
Thread.Sleep(1000);
Console.WriteLine(name);
}
static void Main(string[] args)
{
Thread thread1 = new Thread(PrintName);
Thread thread2 = new Thread(PrintName);
Thread thread3 = new Thread(PrintName);
thread1.Start("Thread1");
thread2.Start("Thread2");
thread3.Start("Thread3");
}
}
실행 결과
My Name is My Name is My Name is Thread1
Thread3
Thread2
실행 결과에서 볼 수 있듯이 원하는 결과가 출력되지 않고 있습니다. 따라서 동기화가 없는 멀티스레딩 환경에서는 공유 리소스가 보호되지 않으며, 애플리케이션 또는 프로그램이 비정상적으로 동작할 수 있습니다.
멀티스레드 동기화 예제
다음 예제는 위 예제와 동일하게 스레드 객체가 동일한 리소스인 PrintName() 메서드를 접근합니다. 하지만, 이번에는 잠금을 사용하여 PrintName() 메서드에 대한 접근이 동기화됩니다.
class Program
{
static object lockObject = new object();
public static void PrintName(Object name)
{
lock (lockObject)
{
Console.Write("My Name is ");
Thread.Sleep(1000);
Console.WriteLine(name);
}
}
static void Main(string[] args)
{
Thread thread1 = new Thread(PrintName);
Thread thread2 = new Thread(PrintName);
Thread thread3 = new Thread(PrintName);
thread1.Start("Thread1");
thread2.Start("Thread2");
thread3.Start("Thread3");
}
}
실행 결과
My Name is Thread1
My Name is Thread2
My Name is Thread3
정리
- 데이터 불일치는 두 개 이상의 스레드 객체가 동시에 공유 리소스를 접근할 때 발생합니다.
- 스레드 동기화를 사용하여 데이터 불일치를 방지할 수 있습니다.
- 잠금은 특정 스레드가 공유 리소스를 사용 중인 경우 다른 스레드가 공유 리소스를 접근할 수 없도록 하는 메커니즘입니다.
'C#' 카테고리의 다른 글
[C#]base 키워드 (0) | 2022.06.05 |
---|---|
[C#]잠금 및 Lock 키워드 (0) | 2022.06.05 |
[C#]지역 함수(Local Function) (0) | 2022.05.29 |
[C#]멀티스레드(MultiThread) (0) | 2022.05.28 |
[C#]Thread 생성자(Thread Constructor) (0) | 2022.05.28 |
댓글