07、使用监视器为锁添加超时功能
使用监视器为锁添加超时功能
知识点
什么是Monitor
Monitor类是.NET Framework中提供的同步原语,它提供了比lock语句更灵活的锁定机制。Monitor允许设置超时时间,避免线程无限期等待锁。
Monitor的优势
- 超时控制:可以设置获取锁的超时时间
- 可中断:支持线程中断
- 灵活性:提供更细粒度的控制
- 死锁预防:通过超时机制避免死锁
Monitor的核心方法
Monitor.Enter(obj):获取锁Monitor.TryEnter(obj, timeout):尝试在指定时间内获取锁Monitor.Exit(obj):释放锁Monitor.Wait(obj):等待条件变量Monitor.Pulse(obj):通知等待的线程Monitor.PulseAll(obj):通知所有等待的线程
超时机制的重要性
- 避免死锁
- 提高系统响应性
- 实现优雅的错误处理
- 支持取消操作
代码案例
案例1:基本的Monitor超时使用
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
private static readonly object lockObject = new object();
private static int sharedResource = 0;
static void Main(string[] args)
{
Console.WriteLine("Monitor超时示例");
// 启动一个长时间持有锁的任务
Task longTask = Task.Run(() => LongRunningTask());
Thread.Sleep(100); // 确保长任务先获得锁
// 启动一个尝试获取锁的任务
Task shortTask = Task.Run(() => TryAcquireLockWithTimeout());
Task.WaitAll(longTask, shortTask);
Console.WriteLine("所有任务完成");
}
static void LongRunningTask()
{
Console.WriteLine("长任务:尝试获取锁...");
lock (lockObject)
{
Console.WriteLine("长任务:获得锁,开始长时间工作...");
Thread.Sleep(5000); // 模拟长时间工作
sharedResource = 100;
Console.WriteLine("长任务:工作完成,释放锁");
}
}
static void TryAcquireLockWithTimeout()
{
Console.WriteLine("短任务:尝试获取锁(3秒超时)...");
bool lockAcquired = false;
try
{
Monitor.TryEnter(lockObject, TimeSpan.FromSeconds(3), ref lockAcquired);
if (lockAcquired)
{
Console.WriteLine("短任务:成功获得锁");
// 执行一些工作
sharedResource += 10;
Console.WriteLine($"短任务:共享资源值 = {sharedResource}");
}
else
{
Console.WriteLine("短任务:获取锁超时,继续其他工作");
// 执行替代逻辑
Console.WriteLine("短任务:执行不需要锁的操作");
}
}
finally
{
if (lockAcquired)
{
Monitor.Exit(lockObject);
Console.WriteLine("短任务:释放锁");
}
}
}
}
案例2:生产者消费者模式with Monitor
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
class ProducerConsumerWithMonitor
{
private static readonly object lockObject = new object();
private static Queue<int> buffer = new Queue<int>();
private static bool isProducing = true;
private const int MaxBufferSize = 5;
static void Main(string[] args)
{
Console.WriteLine("生产者消费者模式(Monitor实现)");
// 启动生产者
Task producer = Task.Run(() => Producer());
// 启动多个消费者
Task[] consumers = new Task[3];
for (int i = 0; i < consumers.Length; i++)
{
int consumerId = i;
consumers[i] = Task.Run(() => Consumer(consumerId));
}
// 运行5秒后停止生产
Thread.Sleep(5000);
isProducing = false;
// 通知所有等待的消费者
lock (lockObject)
{
Monitor.PulseAll(lockObject);
}
Task.WaitAll(new[] { producer }.Concat(consumers).ToArray());
Console.WriteLine("程序结束");
}
static void Producer()
{
int item = 1;
while (isProducing)
{
bool lockAcquired = false;
try
{
Monitor.TryEnter(lockObject, TimeSpan.FromSeconds(1), ref lockAcquired);
if (lockAcquired)
{
// 等待缓冲区有空间
while (buffer.Count >= MaxBufferSize && isProducing)
{
Console.WriteLine("生产者:缓冲区满,等待...");
Monitor.Wait(lockObject, TimeSpan.FromSeconds(1));
}
if (isProducing && buffer.Count < MaxBufferSize)
{
buffer.Enqueue(item);
Console.WriteLine($"生产者:生产了 {item},缓冲区大小:{buffer.Count}");
item++;
// 通知消费者
Monitor.PulseAll(lockObject);
}
}
else
{
Console.WriteLine("生产者:获取锁超时");
}
}
finally
{
if (lockAcquired)
{
Monitor.Exit(lockObject);
}
}
Thread.Sleep(500); // 模拟生产时间
}
Console.WriteLine("生产者:停止生产");
}
static void Consumer(int consumerId)
{
while (true)
{
bool lockAcquired = false;
try
{
Monitor.TryEnter(lockObject, TimeSpan.FromSeconds(2), ref lockAcquired);
if (lockAcquired)
{
// 等待缓冲区有数据
while (buffer.Count == 0 && isProducing)
{
Console.WriteLine($"消费者{consumerId}:缓冲区空,等待...");
Monitor.Wait(lockObject, TimeSpan.FromSeconds(1));
}
if (buffer.Count > 0)
{
int item = buffer.Dequeue();
Console.WriteLine($"消费者{consumerId}:消费了 {item},缓冲区大小:{buffer.Count}");
// 通知生产者
Monitor.PulseAll(lockObject);
}
else if (!isProducing)
{
Console.WriteLine($"消费者{consumerId}:生产结束,退出");
break;
}
}
else
{
Console.WriteLine($"消费者{consumerId}:获取锁超时");
if (!isProducing) break;
}
}
finally
{
if (lockAcquired)
{
Monitor.Exit(lockObject);
}
}
Thread.Sleep(1000); // 模拟消费时间
}
}
}
案例3:死锁检测和恢复
using System;
using System.Threading;
using System.Threading.Tasks;
class DeadlockDetection
{
private static readonly object lock1 = new object();
private static readonly object lock2 = new object();
static void Main(string[] args)
{
Console.WriteLine("死锁检测和恢复示例");
Task task1 = Task.Run(() => SafeMethod1());
Task task2 = Task.Run(() => SafeMethod2());
bool completed = Task.WaitAll(new[] { task1, task2 }, TimeSpan.FromSeconds(10));
if (completed)
{
Console.WriteLine("所有任务成功完成");
}
else
{
Console.WriteLine("检测到可能的死锁或超时");
}
}
static void SafeMethod1()
{
bool lock1Acquired = false;
bool lock2Acquired = false;
try
{
Console.WriteLine("方法1:尝试获取lock1...");
Monitor.TryEnter(lock1, TimeSpan.FromSeconds(2), ref lock1Acquired);
if (lock1Acquired)
{
Console.WriteLine("方法1:获得lock1");
Thread.Sleep(1000); // 模拟工作
Console.WriteLine("方法1:尝试获取lock2...");
Monitor.TryEnter(lock2, TimeSpan.FromSeconds(3), ref lock2Acquired);
if (lock2Acquired)
{
Console.WriteLine("方法1:获得lock2,执行工作");
Thread.Sleep(1000);
Console.WriteLine("方法1:工作完成");
}
else
{
Console.WriteLine("方法1:获取lock2超时,执行替代方案");
// 执行不需要lock2的替代逻辑
}
}
else
{
Console.WriteLine("方法1:获取lock1超时");
}
}
finally
{
if (lock2Acquired)
{
Monitor.Exit(lock2);
Console.WriteLine("方法1:释放lock2");
}
if (lock1Acquired)
{
Monitor.Exit(lock1);
Console.WriteLine("方法1:释放lock1");
}
}
}
static void SafeMethod2()
{
bool lock1Acquired = false;
bool lock2Acquired = false;
try
{
Console.WriteLine("方法2:尝试获取lock2...");
Monitor.TryEnter(lock2, TimeSpan.FromSeconds(2), ref lock2Acquired);
if (lock2Acquired)
{
Console.WriteLine("方法2:获得lock2");
Thread.Sleep(1000); // 模拟工作
Console.WriteLine("方法2:尝试获取lock1...");
Monitor.TryEnter(lock1, TimeSpan.FromSeconds(3), ref lock1Acquired);
if (lock1Acquired)
{
Console.WriteLine("方法2:获得lock1,执行工作");
Thread.Sleep(1000);
Console.WriteLine("方法2:工作完成");
}
else
{
Console.WriteLine("方法2:获取lock1超时,执行替代方案");
// 执行不需要lock1的替代逻辑
}
}
else
{
Console.WriteLine("方法2:获取lock2超时");
}
}
finally
{
if (lock1Acquired)
{
Monitor.Exit(lock1);
Console.WriteLine("方法2:释放lock1");
}
if (lock2Acquired)
{
Monitor.Exit(lock2);
Console.WriteLine("方法2:释放lock2");
}
}
}
}
知识点总结
-
Monitor的核心优势:
- 提供超时机制,避免无限等待
- 支持条件变量(Wait/Pulse)
- 更灵活的锁控制
-
超时策略:
- 合理设置超时时间
- 提供替代执行路径
- 避免过短的超时导致性能问题
-
最佳实践:
- 总是使用try-finally确保锁的释放
- 使用TryEnter而不是Enter来避免死锁
- 合理使用Wait和Pulse进行线程协调
-
性能考虑:
- Monitor比lock稍慢,但提供更多功能
- 避免频繁的超时操作
- 合理设计锁的粒度
-
错误处理:
- 处理超时情况
- 提供备用方案
- 记录和监控锁争用情况

浙公网安备 33010602011771号