C# Monitor 类
介绍
在多线程编程中,多个线程可能会同时访问共享资源,从而导致竞争条件(Race Condition)。为了避免这种情况,C# 提供了 Monitor
类,用于实现线程同步。Monitor
类通过锁定对象来确保同一时间只有一个线程可以访问共享资源。
Monitor
类是 C# 中最基本的线程同步机制之一,它提供了 Enter
、Exit
、Wait
、Pulse
和 PulseAll
等方法,用于控制线程的访问和通信。
Monitor 类的基本用法
Monitor
类的核心功能是通过锁定对象来实现线程同步。以下是 Monitor
类的基本用法:
object lockObject = new object();
void ThreadSafeMethod()
{
lock (lockObject)
{
// 临界区代码
}
}
在上面的代码中,lock
语句实际上是 Monitor
类的语法糖。它等价于以下代码:
object lockObject = new object();
void ThreadSafeMethod()
{
Monitor.Enter(lockObject);
try
{
// 临界区代码
}
finally
{
Monitor.Exit(lockObject);
}
}
代码示例
以下是一个简单的示例,展示了如何使用 Monitor
类来保护共享资源:
using System;
using System.Threading;
class Program
{
private static object lockObject = new object();
private static int sharedResource = 0;
static void Main(string[] args)
{
Thread thread1 = new Thread(IncrementResource);
Thread thread2 = new Thread(IncrementResource);
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
Console.WriteLine("Final value of sharedResource: " + sharedResource);
}
static void IncrementResource()
{
for (int i = 0; i < 100000; i++)
{
Monitor.Enter(lockObject);
try
{
sharedResource++;
}
finally
{
Monitor.Exit(lockObject);
}
}
}
}
输出:
Final value of sharedResource: 200000
在这个示例中,两个线程同时尝试增加 sharedResource
的值。通过使用 Monitor
类,我们确保了每次只有一个线程可以访问 sharedResource
,从而避免了竞争条件。
Monitor 类的高级用法
除了基本的锁定功能,Monitor
类还提供了 Wait
、Pulse
和 PulseAll
方法,用于线程间的通信。
Wait 方法
Wait
方法使当前线程释放锁并进入等待状态,直到其他线程调用 Pulse
或 PulseAll
方法唤醒它。
Pulse 和 PulseAll 方法
Pulse
方法唤醒一个等待的线程,而 PulseAll
方法唤醒所有等待的线程。
代码示例
以下是一个使用 Wait
和 Pulse
的示例,展示了如何实现生产者-消费者模式:
using System;
using System.Collections.Generic;
using System.Threading;
class Program
{
private static object lockObject = new object();
private static Queue<int> queue = new Queue<int>();
static void Main(string[] args)
{
Thread producer = new Thread(Produce);
Thread consumer = new Thread(Consume);
producer.Start();
consumer.Start();
producer.Join();
consumer.Join();
}
static void Produce()
{
for (int i = 0; i < 10; i++)
{
Monitor.Enter(lockObject);
try
{
queue.Enqueue(i);
Console.WriteLine("Produced: " + i);
Monitor.Pulse(lockObject);
}
finally
{
Monitor.Exit(lockObject);
}
Thread.Sleep(100);
}
}
static void Consume()
{
while (true)
{
Monitor.Enter(lockObject);
try
{
while (queue.Count == 0)
{
Monitor.Wait(lockObject);
}
int item = queue.Dequeue();
Console.WriteLine("Consumed: " + item);
}
finally
{
Monitor.Exit(lockObject);
}
}
}
}
输出:
Produced: 0
Consumed: 0
Produced: 1
Consumed: 1
...
Produced: 9
Consumed: 9
在这个示例中,生产者线程将数据放入队列,而消费者线程从队列中取出数据。通过使用 Wait
和 Pulse
,我们确保了消费者线程在队列为空时进入等待状态,直到生产者线程添加新数据并唤醒它。
实际应用场景
Monitor
类在实际应用中有很多用途,例如:
- 资源池管理:在多线程环境中管理有限的资源(如数据库连接池)。
- 任务队列:实现生产者-消费者模式,处理异步任务。
- 线程间通信:协调多个线程的执行顺序。
总结
Monitor
类是 C# 中实现线程同步的重要工具。通过使用 Monitor
类,我们可以避免多线程环境中的竞争条件,并实现线程间的通信。掌握 Monitor
类的基本用法和高级功能,对于编写高效、安全的多线程程序至关重要。
附加资源与练习
- 练习:尝试修改生产者-消费者示例,使其支持多个生产者和消费者。
- 资源:阅读 C# 官方文档 以了解更多关于
Monitor
类的详细信息。
在多线程编程中,始终确保在 finally
块中释放锁,以避免死锁。