Monitor 类通过向单个线程授予对象锁来控制对对象的访问。 对象锁提供限制访问代码块(通常称为临界区)的能力。当一个线程拥有对象的锁时,其他任何线程都不能获取该锁。还可以使用 Monitor 来确保不会允许其他任何线程访问正在由锁的所有者执行的应用程序代码节,除非另一个线程正在使用其他的锁定对象执行该代码。
使用 Monitor 锁定对象(即引用类型)而不是值类型。
Monitor 具有以下功能:
它根据需要与某个对象相关联。
它是未绑定的,也就是说可以直接从任何上下文调用它。
不能创建 Monitor 类的实例。
将为每个同步对象来维护以下信息:
对当前持有锁的线程的引用。
对就绪队列的引用,它包含准备获取锁的线程。
对等待队列的引用,它包含正在等待锁定对象状态变化通知的线程。——MSDN
使用 Enter 和 Exit 方法标记临界区的开头和结尾。 如果临界区是一个连续指令集,则由 Enter 方法获取的锁将保证只有一个线程可以使用锁定对象执行所包含的代码。 在这种情况下,建议您将这些指令放在 try 块中,并将 Exit 指令放在 finally 块中。 此功能通常用于同步对类的静态或实例方法的访问。如果实例方法需要同步线程访问,则它将使用当前实例作为要锁定的对象调用 Enter 和对应的 Exit 方法。 由于只能有一个线程持有当前实例上的锁,因此该方法一次只能由一个线程来执行。静态方法是使用当前实例的 Type 作为锁定对象以类似的方式来保护的。 Enter 和 Exit 方法提供的功能与 C# lock 语句提供的功能相同,区别在于 lock 和 SyncLock 以 try… finally 块(在 Visual Basic 中为 Try… Finally)来包装 Enter 方法重载,以确保释放监视器。
从 .NET Framework 4 版 开始,有两组用于 Enter 和 TryEnter 方法的重载。 一组重载具有一个 ref Boolean 参数,在获取锁定时自动设置为 true,即使在获取锁定时引发了异常。 如果释放在所有事例中的锁定非常重要,即使在该锁定保护的资源的状态可能不一致时,也请使用这些重载。
如果临界区跨越整个方法,则可以通过将 System.Runtime.CompilerServices.MethodImplAttribute 放置在方法上并在 MethodImplAttribute 的构造函数中指定 Synchronized 值来实现上述锁定功能。 使用该属性后就不需要 Enter 和 Exit 语句了。 请注意,该属性将使当前线程持有锁,直到方法返回;如果可以更早释放锁,则使用 Monitor 类或 C# lock 语句而不是该属性。
尽管锁定和释放给定对象的 Enter 和 Exit 语句可以跨越成员或类的边界或同时跨越两者的边界,但并不推荐这样做。
当选择要同步的对象时,应只锁定私有或内部对象。锁定外部对象可能导致死锁,这是因为不相关的代码可能会出于不同的目的而选择锁定相同的对象。——MSDN
class="csharpcode">using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.Remoting.Messaging; using System.Text; using System.Threading; namespace ConsoleApplication { class Program { static ProducerConsumer objProducerConsumer = null; static void Main(string[] args) { objProducerConsumer = new ProducerConsumer(); Thread _currenThread = new Thread(new ThreadStart(ProcessConsumerJob)); _currenThread.Start(); ProcessProducerJob(); } static void ProcessProducerJob() { for (int i = 0; i < 10; i++) { Console.WriteLine(string.Format("ProducerJob {0}.", i)); objProducerConsumer.Producer(i); Thread.Sleep(500); } } static void ProcessConsumerJob() { for (int i = 0; i < 10; i++) { Console.WriteLine(string.Format("\t\t\tConsumerJob {0}.", objProducerConsumer.Consumer())); Thread.Sleep(500); } } } public class ProducerConsumer { private static readonly object syncRoot = new object(); private Queue<int> queuePC = new Queue<int>(); public void Producer(int _nNumber) { Monitor.Enter(syncRoot); try { queuePC.Enqueue(_nNumber); Monitor.Pulse(syncRoot); } finally { Monitor.Exit(syncRoot); } } public int Consumer() { Monitor.Enter(syncRoot); try { while (queuePC.Count == 0) Monitor.Wait(syncRoot); return queuePC.Dequeue(); } finally { Monitor.Exit(syncRoot); } } } }
http://www.yoda.arachsys.com/csharp/threads/deadlocks.shtml