C Sharp Monitor 类
外观
C# Monitor类[编辑 | 编辑源代码]
Monitor类是C#中用于线程同步的核心类之一,属于System.Threading命名空间。它提供了一种机制,允许线程在访问共享资源时进行互斥锁定,从而避免竞态条件(Race Condition)和数据不一致问题。Monitor类是基于临界区(Critical Section)的概念实现的,确保同一时间只有一个线程可以执行特定的代码块。
概述[编辑 | 编辑源代码]
Monitor类通过Enter和Exit方法实现锁定和解锁操作,通常与lock关键字(语法糖)结合使用。其核心功能包括:
- 互斥访问:确保共享资源在同一时间仅被一个线程访问。
- 线程等待与通知:通过Wait、Pulse和PulseAll方法实现线程间的协作。
基本用法[编辑 | 编辑源代码]
使用 lock 关键字[编辑 | 编辑源代码]
C#中的`lock`关键字是Monitor类的简化写法。以下示例展示如何通过`lock`保护共享资源:
using System;
using System.Threading;
class Program
{
private static readonly object _lock = new object();
private static int _counter = 0;
static void Main()
{
for (int i = 0; i < 5; i++)
{
new Thread(IncrementCounter).Start();
}
Thread.Sleep(1000);
Console.WriteLine($"Final Counter: {_counter}");
}
static void IncrementCounter()
{
lock (_lock)
{
_counter++;
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: Counter = {_counter}");
}
}
}
输出示例:
Thread 3: Counter = 1 Thread 4: Counter = 2 Thread 5: Counter = 3 Thread 6: Counter = 4 Thread 7: Counter = 5 Final Counter: 5
直接使用 Monitor 类[编辑 | 编辑源代码]
等效的Monitor实现如下:
static void IncrementCounter()
{
Monitor.Enter(_lock);
try
{
_counter++;
Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: Counter = {_counter}");
}
finally
{
Monitor.Exit(_lock);
}
}
关键点:
- 必须使用`try-finally`确保锁被释放。
- 若未释放锁会导致死锁。
线程协作:Wait 和 Pulse[编辑 | 编辑源代码]
Monitor类支持更复杂的线程交互模式。以下案例模拟生产者-消费者问题:
class Buffer
{
private readonly object _lock = new object();
private Queue<int> _queue = new Queue<int>();
private const int MaxSize = 3;
public void Produce(int value)
{
Monitor.Enter(_lock);
try
{
while (_queue.Count >= MaxSize)
{
Monitor.Wait(_lock); // 释放锁并等待
}
_queue.Enqueue(value);
Console.WriteLine($"Produced: {value}");
Monitor.Pulse(_lock); // 通知等待线程
}
finally
{
Monitor.Exit(_lock);
}
}
public int Consume()
{
Monitor.Enter(_lock);
try
{
while (_queue.Count == 0)
{
Monitor.Wait(_lock);
}
int value = _queue.Dequeue();
Console.WriteLine($"Consumed: {value}");
Monitor.Pulse(_lock);
return value;
}
finally
{
Monitor.Exit(_lock);
}
}
}
执行流程图:
注意事项[编辑 | 编辑源代码]
1. 锁对象选择:应使用`private readonly object`作为锁对象,避免使用值类型或`this`。 2. 死锁风险:确保锁在所有路径(包括异常)下都能释放。 3. 性能影响:频繁的锁竞争会降低并发性能。
实际应用场景[编辑 | 编辑源代码]
- 数据库连接池:管理有限连接的分配。
- 日志系统:避免多线程写入日志文件时的冲突。
- 缓存更新:确保缓存数据的原子性更新。
数学原理[编辑 | 编辑源代码]
Monitor的实现基于Dijkstra信号量(Semaphore)理论,其等待队列模型可表示为: 其中`S`为信号量计数器。
进阶话题[编辑 | 编辑源代码]
- Monitor vs Mutex:Monitor是轻量级的,仅在同一进程内有效;Mutex支持跨进程。
- Monitor.TryEnter:支持超时机制,避免无限等待。