Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C#

ความแตกต่างระหว่าง Monitor และ Lock ใน C#

ทั้ง Monitor และ lock จัดเตรียม Mechanism สำหรับ Synchronize Access ไปยัง Objects lock เป็น Shortcut สำหรับ Monitor.Enter พร้อม try และ finally

lock เป็น Shortcut และเป็น Option สำหรับ Basic Usage ถ้าต้องการ Control มากขึ้นสำหรับ Advanced Multithreading Solutions ใช้ TryEnter(), Wait(), Pulse(), PulseAll() Methods แล้ว Montior Class คือ Option ของคุณ

ตัวอย่าง Lock


class Program {
    static object _lock = new object();
    static int Total;
    
    public static void Main() {
        AddOneHundredLock();
        Console.ReadLine();
    }
    
    public static void AddOneHundredLock() {
        for (int i = 1; i <= 100; i++) {
            lock (_lock) {
                Total++;
            }
        }
    }
}

ตัวอย่าง Monitor


class Program {
    static object _lock = new object();
    static int Total;
    
    public static void Main() {
        AddOneHundredMonitor();
        Console.ReadLine();
    }
    
    public static void AddOneHundredMonitor() {
        for (int i = 1; i <= 100; i++) {
            Monitor.Enter(_lock);
            try {
                Total++;
            }
            finally {
                Monitor.Exit(_lock);
            }
        }
    }
}

Monitor Advanced Methods

TryEnter - Non-blocking Lock Attempt


public bool TryUpdateWithTimeout(int timeoutMs) {
    // ลอง Lock ภายใน Timeout ไม่ Block แบบนิรันดร์
    if (Monitor.TryEnter(_lock, timeoutMs)) {
        try {
            // Critical Section
            return true;
        }
        finally {
            Monitor.Exit(_lock);
        }
    }
    return false; // Lock ไม่ได้ภายใน Timeout
}

Wait/Pulse - Thread Signaling


class BoundedBuffer {
    private readonly Queue _queue = new Queue();
    private readonly int _capacity;
    private readonly object _lock = new object();
    
    public void Produce(T item) {
        lock (_lock) {
            while (_queue.Count >= _capacity) {
                Monitor.Wait(_lock); // Release Lock และ Wait
            }
            _queue.Enqueue(item);
            Monitor.Pulse(_lock); // Notify One Waiting Consumer
        }
    }
    
    public T Consume() {
        lock (_lock) {
            while (_queue.Count == 0) {
                Monitor.Wait(_lock); // Release Lock และ Wait
            }
            var item = _queue.Dequeue();
            Monitor.Pulse(_lock); // Notify One Waiting Producer
            return item;
        }
    }
}

PulseAll - Notify All Waiting Threads


// ใช้เมื่อ State Change อาจส่งผลต่อ Multiple Waiting Threads
Monitor.PulseAll(_lock);

เปรียบเทียบ Lock vs Monitor

FeaturelockMonitor
SyntaxSimple (Syntactic Sugar)Verbose (Explicit Enter/Exit)
Exception SafetyAuto (try/finally Generated)Manual (ต้องเขียน try/finally เอง)
TryEnter (Timeout)ไม่ได้ได้
Wait/Pulse/PulseAllไม่ได้ได้
Readabilityสูงต่ำกว่า
Performanceเหมือนกัน (Compile to Monitor)Baseline

lock Compiles To


// Source
lock (obj) {
    DoWork();
}

// Compiled (Conceptually)
var temp = obj;
Monitor.Enter(temp);
try {
    DoWork();
}
finally {
    Monitor.Exit(temp);
}

Best Practices

  • ใช้ lock สำหรับ Simple Mutual Exclusion (90%+ ของ Cases)
  • ใช้ Monitor.TryEnter เมื่อต้องการ Timeout หรือ Non-blocking
  • ใช้ Monitor.Wait/Pulse สำหรับ Producer-Consumer Patterns
  • Lock Object ควรเป็น private readonly object เฉพาะสำหรับ Locking
  • หลีกเลี่ยง lock บน this, typeof(Type), String Literals
  • Critical Section ควรสั้นที่สุดเท่าที่จะเป็นไปได้

ใน .NET Modern: 他の選択肢

  • SemaphoreSlim - Async-friendly, Lightweight
  • ReaderWriterLockSlim - Multiple Readers, Single Writer
  • Interlocked - Atomic Operations (Counters, Flags)
  • Concurrent Collections - Thread-safe Collections (Dictionary, Queue, Bag)
  • Channels - Producer-Consumer (System.Threading.Channels)