logo MSJO.kr

Thread Synchronization in C#

2020-09-05
MsJ
 

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.
Reference
  1. CODELLIGENT, Thread Synchronization in C# .Net made easy!
  2. 재:편집 블로그, 세마포어와 뮤텍스
  3. TechDifferences, Difference Between Semaphore and Mutex

Prεv(Θld)   Nεxt(Nεw)
Content
Search     RSS Feed     BY-NC-ND