- Lock
- Monitor
- Manual Reset Event
- Auto Reset Event
- Mutex
- Semaphore
- Comparison Semaphore Vs Mutex
- Reference
Thread를 다룰 때 Synchronization(동기화) 가 필요한 경우가 있다. 특히 ‘Critical section’의 이해는 중요한 데 여러 프로세스가 데이터를 공유할 때 각 프로세스에서 공유 데이터를 액세스하는 것을 제한해야 한다. 이번 예제는 C#언어에서 Thread Synchronization을 다루는 ‘Lock’, ‘Monitor’, ‘Manual Reset Event’, ‘Auto Reset Event’, ‘Mutex’, ‘Semaphore’에 관한 간단한 소스이다1.
Lock
using System;
using System.Threading;
namespace ConsoleExam
{
internal class Program
{
private static readonly object _locker = new object();
private static void Main()
{
for (var i = 0; i < 5; i++)
{
new Thread(DoWork).Start();
}
Console.ReadKey(true);
}
public static void DoWork()
{
lock (_locker)
{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} starting...");
Thread.Sleep(1000);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} completed...");
}
}
}
}
Monitor
using System;
using System.Threading;
namespace ConsoleExam
{
internal class Program
{
private static readonly object _locker = new object();
private static void Main()
{
for (var i = 0; i < 5; i++)
{
new Thread(DoWork).Start();
}
Console.ReadKey(true);
}
public static void DoWork()
{
try
{
Monitor.Enter(_locker);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} starting...");
Thread.Sleep(1000);
//throw new Exception($"Exception : {Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} completed...");
}
catch (Exception ex)
{
Console.WriteLine($"Exception : {ex.Message}");
}
finally
{
Monitor.Exit(_locker);
}
}
}
}
Manual Reset Event
using System;
using System.Threading;
namespace ConsoleExam2
{
internal class Program
{
private static readonly ManualResetEvent _mre = new ManualResetEvent(false);
private static void Main()
{
new Thread(Write).Start();
for (var i = 0; i < 5; i++)
{
new Thread(Read).Start();
}
Console.ReadKey(true);
}
public static void Write()
{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing...");
_mre.Reset();
Thread.Sleep(2000);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing Completed...");
_mre.Set();
}
public static void Read()
{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Waiting...");
_mre.WaitOne();
//Thread.Sleep(1000);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Reading...");
}
}
}
Auto Reset Event
using System;
using System.Threading;
namespace ConsoleExam2
{
internal class Program
{
private static readonly AutoResetEvent _are = new AutoResetEvent(true);
private static void Main()
{
for (var i = 0; i < 5; i++)
{
new Thread(Write).Start();
}
//Thread.Sleep(2000);
//_are.Set();
Console.ReadKey(true);
}
public static void Write()
{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Waiting...");
_are.WaitOne();
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing...");
Thread.Sleep(2000);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing Completed...");
_are.Set();
}
}
}
Mutex
using System;
using System.Threading;
namespace ConsoleExam2
{
internal class Program
{
private static readonly Mutex _mutex = new Mutex();
private static void Main()
{
for (var i = 0; i < 5; i++)
{
new Thread(Write).Start();
}
Console.ReadKey(true);
}
public static void Write()
{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Waiting...");
_mutex.WaitOne();
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing...");
Thread.Sleep(2000);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing Completed...");
_mutex.ReleaseMutex();
}
}
}
Semaphore
using System;
using System.Threading;
namespace ConsoleExam2
{
internal class Program
{
private static readonly Semaphore _semaphore = new Semaphore(1, 1);
private static void Main()
{
for (var i = 0; i < 5; i++)
{
new Thread(Write).Start();
}
Console.ReadKey(true);
}
public static void Write()
{
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Waiting...");
_semaphore.WaitOne();
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing...");
Thread.Sleep(2000);
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} Writing Completed...");
_semaphore.Release();
}
}
}
/* Async Waiting inside C# Locks
static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1,1);
await semaphoreSlim.WaitAsync();
try
{
await Task.Delay(1000);
}
finally
{
semaphoreSlim.Release();
}
*/
Comparison Semaphore Vs Mutex
세마포어와 뮤텍스의 차이는 간단한 정의는 세마포어는 공유된 자원의 데이터를 여러 프로세스가 접근하는 것을 막는 것이고, 뮤텍스는 여러 스레드가 접근을 막는 것이라고 할 수 있다2. 아래의 차트는 세마포어와 뮤텍스의 비교표이다3.
BASIS FOR COMPARISON | SEMAPHORE | MUTEX |
---|---|---|
Basic | Semaphore is a signalling mechanism. | Mutex is a locking mechanism. |
Existence | Semaphore is an integer variable. | Mutex is an object. |
Function | Semaphore allow multiple program threads to access a finite instance of resources. | Mutex allow multiple program thread to access a single resource but not simultaneously. |
Ownership | Semaphore value can be changed by any process acquiring or releasing the resource. | Mutex object lock is released only by the process that has acquired the lock on it. |
Categorize | Semaphore can be categorized into counting semaphore and binary semaphore. | Mutex is not categorized further. |
Operation | Semaphore value is modified using wait() and signal() operation. | Mutex object is locked or unlocked by the process requesting or releasing the resource. |
Resources Occupied | If all resources are being used, the process requesting for resource performs wait() operation and block itself till semaphore count become greater than one. | If a mutex object is already locked, the process requesting for resources waits and queued by the system till lock is released. |