C# 中 Monitor 的多线程应用
admin
2025年1月26日 9:35
本文热度 352
在C#的多线程编程中, Monitor
是一种用于同步多个线程访问共享资源的机制。 它是基于对象的锁定机制,能够有效地控制对代码块的访问,防止数据的不一致,其实与lock基本一样的。 本文将详细介绍 Monitor
的特点、用法,并提供多个示例以展示其应用。
`Monitor` 的特点 独占性访问 :Monitor
通过锁定对象,确保同一时刻只有一个线程可以访问被锁定的代码块。
高效性 :相比于 Mutex
,Monitor
的性能开销较小,适合在同一进程中的多线程环境中使用。
支持条件变量 :Monitor
允许线程在等待某个条件时释放锁,这样其他线程可以获得锁,避免资源的浪费。
易于使用 :Monitor
提供了较为简单的 APIs,如 Enter
、Exit
、Wait
、Pulse
和 PulseAll
。
使用 `Monitor` 的基本语法 以下是 Monitor
的基本用法示例:
object lockObject = new object(); Monitor.Enter(lockObject); // 请求锁 try { // ... 访问共享资源 } finally { Monitor.Exit(lockObject); // 释放锁 }
示例:使用 `Monitor` 实现线程安全的计数器 以下示例展示了如何使用 Monitor
来实现一个线程安全的计数器,确保只有一个线程可以对计数器进行更新。
namespace AppMonitor01 { internal class Program { private static int counter = 0 ; // 共享资源 private static object lockObject = new object(); // 用于锁定代码块 static void Main (string [] args) { Thread[] threads = new Thread[5 ]; for (int i = 0 ; i < threads.Length; i++) { threads[i] = new Thread(IncrementCounter); threads[i].Start(); } foreach (var thread in threads) { thread.Join(); } Console.WriteLine($"最终计数器的值: {counter}" ); } static void IncrementCounter () { for (int i = 0 ; i < 1000 ; i++) { Monitor.Enter(lockObject); // 请求锁 try { counter++; // 增加计数 } finally { Monitor.Exit(lockObject); // 确保释放锁 } } } } }
代码解析 共享资源 :counter
是一个静态变量,由多个线程共享。
锁对象 :lockObject
是一个用于同步的对象,所有线程通过该对象进行控制。
线程创建和启动 :创建5个线程并启动它们,每个线程执行 IncrementCounter
方法。
计数器增加 :在 Monitor.Enter(lockObject)
后,只有获得锁的线程才能执行 counter++
。
锁的释放 :在 finally
块内调用 Monitor.Exit(lockObject)
,确保锁总是释放,即使发生异常。
等待线程完成 :主线程使用 Join
方法确保所有工作线程完成后再输出 counter
的值。
示例:使用 `Monitor` 的条件变量 使用 Monitor
的条件变量,允许线程在特定条件下等待以释放锁,以下示例展示了如何使用 Monitor.Wait
和 Monitor.Pulse
。
using System;using System.Threading;class Program { private static object lockObject = new object(); // 用于同步的对象 private static bool isReady = false ; // 条件变量 static void Main (string [] args) { Thread workerThread = new Thread(Worker); Thread notifierThread = new Thread(Notifier); workerThread.Start(); notifierThread.Start(); workerThread.Join(); notifierThread.Join(); } static void Worker () { Console.WriteLine("Worker 正在等待通知..." ); lock (lockObject) { while (!isReady) { Monitor.Wait(lockObject); // 等待通知 } Console.WriteLine("Worker 收到通知,开始工作。" ); } } static void Notifier () { Console.WriteLine("Notifier 正在处理..." ); Thread.Sleep(2000 ); // 模拟处理时间 lock (lockObject) { isReady = true ; Monitor.Pulse(lockObject); // 发送通知 Console.WriteLine("Notifier 已发送通知。" ); } } }
代码解析 条件变量 :isReady
是一个布尔变量,用于控制 Worker
线程的执行。
工作线程 :Worker
线程在获得锁后,检查 isReady
的值,如果为 false
,则调用 Monitor.Wait
方法,释放锁并等待通知。
通知线程 :Notifier
线程在处理完毕后,通过 Monitor.Pulse
发送通知,唤醒等待的 Worker
线程。
工作执行 :一旦 Worker
收到通知,就会继续执行相应的工作。
示例:使用 `Monitor.PulseAll` 进行唤醒所有等待线程 在某些情况下,可能需要唤醒所有等待的线程,可以使用 Monitor.PulseAll
方法。以下示例展示了如何使用 Monitor.PulseAll
。
using System;using System.Threading;class Program { private static object lockObject = new object(); private static int readyCount = 0 ; // 准备好的线程计数 private static int neededCount = 3 ; // 需要满多少个线程 static void Main (string [] args) { for (int i = 0 ; i < 5 ; i++) { Thread thread = new Thread(Worker); thread.Start(i + 1 ); } Thread.Sleep(2000 ); // 等待一些时间让所有线程开始 NotifyThreads(); // 调用通知方法 } static void Worker (object index) { Console.WriteLine($"线程 {index} 正在准备..." ); lock (lockObject) { readyCount++; if (readyCount < neededCount) { Monitor.Wait(lockObject); // 等待通知 } Console.WriteLine($"线程 {index} 继续执行。" ); } } static void NotifyThreads () { Console.WriteLine("准备好所有线程。" ); lock (lockObject) { // 使得所有等待的线程都能继续 Monitor.PulseAll(lockObject); } } }
代码解析 准备计数 :readyCount
用于计算已准备好的线程数量,而 neededCount
定义了需要满的线程数量。
工作线程 :每个 Worker
在线程启动时增加 readyCount
计数,如果未达到需要数量的线程,则调用 Monitor.Wait
进入等待状态。
通知方法 :当 NotifyThreads
被调用时,会使用 Monitor.PulseAll
唤醒所有等待的线程,使它们能够继续执行。
使用 `Monitor` 的注意事项 确保释放锁 :始终使用 try…finally
構造以确保在异常或早期返回时仍然释放锁。
避免死锁 :在使用 Monitor
时,确保不以不当顺序获取多个锁,避免死锁现象。
高并发下的性能 :虽然 Monitor
的性能相对较好,但在高并发情况下,可能仍会造成性能瓶颈。
条件变量的使用 :使用 Monitor.Wait
和 Monitor.Pulse
时,确保它们在相同的锁定对象上调用,以确保正确执行。
结论 Monitor
是C#中一种强大的多线程同步机制,能够有效管理线程对共享资源的访问。通过上述示例,您可以看到如何利用 Monitor
来实现线程安全的操作,并控制线程的执行顺序。合理地使用 Monitor
可以显著提高多线程应用的可靠性和效率。在应用中,开发者应关注性能、错误处理及可能的竞争条件,以确保安全和高效性。
该文章在 2025/1/26 9:35:08 编辑过