ภาพรวม
Thread Synchronization ป้องกัน Race Condition เมื่อหลายเธรดเข้าถึง Shared Resource เครื่องมือหลัก: lock, Mutex, Monitor, Semaphore, ReaderWriterLockSlim
1. lock Statement (Monitor) - พื้นฐานที่สุด
public class BankAccount {
private readonly object _lock = new object();
private decimal _balance = 0;
public void Deposit(decimal amount) {
lock (_lock) {
_balance += amount;
}
}
public void Withdraw(decimal amount) {
lock (_lock) {
if (_balance >= amount) _balance -= amount;
}
}
public decimal Balance {
get {
lock (_lock) return _balance;
}
}
}
2. Mutex - Cross-Process Synchronization
using System;
using System.Threading;
public class SingleInstanceApp {
private static Mutex _mutex;
public static void Main() {
bool createdNew;
_mutex = new Mutex(true, "Global\\MyUniqueAppName", out createdNew);
if (!createdNew) {
Console.WriteLine("Application already running.");
return;
}
try {
Console.WriteLine("Running... Press Enter to exit.");
Console.ReadLine();
}
finally {
_mutex.ReleaseMutex();
}
}
}
3. ThreadPool - จัดการ Thread ให้อัตโนมัติ
using System;
using System.Threading;
public class ThreadPoolDemo {
public static void Main() {
Console.WriteLine("Queueing work items...");
for (int i = 0; i < 5; i++) {
int taskNum = i;
ThreadPool.QueueUserWorkItem(DoWork, taskNum);
}
Console.WriteLine("Main thread sleeping...");
Thread.Sleep(2000);
Console.WriteLine("Done.");
}
static void DoWork(object state) {
int taskNum = (int)state;
Console.WriteLine($"Task {taskNum} started on thread {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(500);
Console.WriteLine($"Task {taskNum} completed");
}
}
4. ReaderWriterLockSlim - หลาย Reader, หนึ่ง Writer
using System;
using System.Threading;
public class SharedCache {
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private readonly Dictionary<string, string> _cache = new Dictionary<string, string>();
public string Get(string key) {
_lock.EnterReadLock();
try {
return _cache.TryGetValue(key, out var value) ? value : null;
}
finally {
_lock.ExitReadLock();
}
}
public void Set(string key, string value) {
_lock.EnterWriteLock();
try {
_cache[key] = value;
}
finally {
_lock.ExitWriteLock();
}
}
}
5. SemaphoreSlim - จำกัดจำนวน Concurrent Access
using System;
using System.Threading;
using System.Threading.Tasks;
public class RateLimiter {
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(3); // Max 3 concurrent
public async Task AccessResourceAsync() {
await _semaphore.WaitAsync();
try {
Console.WriteLine($"Access granted. Thread: {Thread.CurrentThread.ManagedThreadId}");
await Task.Delay(1000); // Simulate work
}
finally {
_semaphore.Release();
}
}
}
เปรียบเทียบ Synchronization Primitives
| Primitive | Use Case | Cross-Process | Performance |
|---|---|---|---|
| lock (Monitor) | General purpose, short critical sections | No | Fast |
| Mutex | Cross-process, single instance app | Yes | Slower |
| ReaderWriterLockSlim | Read-heavy workloads | No | Good for reads |
| SemaphoreSlim | Limit concurrent access (throttling) | No | Good |
| Interlocked | Atomic operations on primitives | No | Fastest |
| Concurrent Collections | Thread-safe data structures | No | Best for collections |
Best Practices
- Lock object ต้องเป็น private readonly object
- Critical section ควรสั้นที่สุด (ไม่ควร I/O, Network calls ภายใน lock)
- หลีกเลี่ยง Nested Locks (Deadlock risk)
- ใช้
try/finallyหรือlockstatement เพื่อให้ปล่อย lock แน่ใจ - พิจารณา Concurrent Collections ก่อนเขียน Lock เอง