Monitor类用于同步代码区,其方法是使用Monitor.Enter()方法获得一个锁,然后,使用Monitor.Exit()方法释放该锁。
一个线程获得锁,其他线程就要等到该锁释放后才能使用。一旦在代码区上获取了一个锁,就可以在Monitor.Enter()和Monitor.Exit()程序块内使用如下方法:
Wait()-此方法用于释放对象上的锁,并暂停当前线程,直到它重新获得锁。
Pulse()-此方法用于通知 正在队列中等待的线程,对象的状态已经改变。
PulseAll()-此方法用于通知所有正在队列中等待的线程,对象的状态已经有了改变。
锁的机制主要是帮助帮助线程间的通信。如果一个线程进入对象的关键代码段,需要一定的条件才能存在,而另一线程可以在该代码中创建该条件,此时就需要这种机制。
在任一时刻,关键代码段只允许一具线程访问。当每一个线程进入代码段后,其他的线程就不能再进入了。
下面是一个使用Enter()和Exit()地的示例:
Wait()和Pulse()机制
Wait()和Pulse()机制用于线程间的交互。当在一个对象上执行Wait()时,正在访问该对象的线程就会进入等待状态。直到它得到一个唤醒信号。Pulse()和PulseAll()用于给等待线程发送信号。
注意,只能在Enter()和Exit()代码块中调用Wait()和Pulse()方法。
TryEnter()方法
Monitor类的TryEnter()方法非常类似于Enter()方法,它试图获得对象的独占锁,不过它不会像Enter()方法那样暂停。如果线程成功进入,则TryEnter()方法返回True。
例如ISP的拨号连接,假定有两个应用程序A和B,它们要使用同一个调制解调器拨号连接ISP。目前只有一个可用的调制解调器连接,而且一旦建立起连接,就无法知道连接上的程序会在网上呆多久。假定应用程序A先拨号连接上ISP,过了一段时间后,应用程序B也想拨号连接; 应用程序B可能会无限期地等待下去,因为我们不知道应用程序A要在网上呆多久。在这种情况下,应用程序B就可以使用TryEnter()来确定调制解调器是否已被其他应用程序(本例是A)锁定,而不是无限期地等待下去。
LOCK语句
Lock关键字可以用作Monitor方法的一个替换用法。下面例子输出结果与上面一样:
一个线程获得锁,其他线程就要等到该锁释放后才能使用。一旦在代码区上获取了一个锁,就可以在Monitor.Enter()和Monitor.Exit()程序块内使用如下方法:
Wait()-此方法用于释放对象上的锁,并暂停当前线程,直到它重新获得锁。
Pulse()-此方法用于通知 正在队列中等待的线程,对象的状态已经改变。
PulseAll()-此方法用于通知所有正在队列中等待的线程,对象的状态已经有了改变。
锁的机制主要是帮助帮助线程间的通信。如果一个线程进入对象的关键代码段,需要一定的条件才能存在,而另一线程可以在该代码中创建该条件,此时就需要这种机制。
在任一时刻,关键代码段只允许一具线程访问。当每一个线程进入代码段后,其他的线程就不能再进入了。
下面是一个使用Enter()和Exit()地的示例:
using
System;
using System.Threading;
using System.Runtime.CompilerServices;
namespace thread
{
public class MethodImpl
{
public class EnterExit
{
private int result = 0;
//锁定的代码段
public void CriticalSection()
{
//Enter the Critical Section
Monitor.Enter(this);
Console.WriteLine("Entered Thread" + Thread.CurrentThread.GetHashCode());
for (int i = 1; i <= 5; i++)
{
Console.WriteLine("Result=" + result++ + "ThreadID" + Thread.CurrentThread.GetHashCode());
Thread.Sleep(1000);
}
Console.WriteLine("Exiting Thread" + Thread.CurrentThread.GetHashCode());
//Exit the critical Section
Monitor.Exit(this);
}
public static void Main(string[] args)
{
EnterExit e = new EnterExit();
Thread ctl = new Thread(new ThreadStart(e.CriticalSection));
ctl.Start();
Thread ct2 = new Thread(new ThreadStart(e.CriticalSection));
ct2 .Start();
}
}
}
}
参数this指定应将锁保存在当前对象上。在需要锁定对象,不让其他线程访问它时,应把this指针作为参数和传递。
using System.Threading;
using System.Runtime.CompilerServices;
namespace thread
{
public class MethodImpl
{
public class EnterExit
{
private int result = 0;
//锁定的代码段
public void CriticalSection()
{
//Enter the Critical Section
Monitor.Enter(this);
Console.WriteLine("Entered Thread" + Thread.CurrentThread.GetHashCode());
for (int i = 1; i <= 5; i++)
{
Console.WriteLine("Result=" + result++ + "ThreadID" + Thread.CurrentThread.GetHashCode());
Thread.Sleep(1000);
}
Console.WriteLine("Exiting Thread" + Thread.CurrentThread.GetHashCode());
//Exit the critical Section
Monitor.Exit(this);
}
public static void Main(string[] args)
{
EnterExit e = new EnterExit();
Thread ctl = new Thread(new ThreadStart(e.CriticalSection));
ctl.Start();
Thread ct2 = new Thread(new ThreadStart(e.CriticalSection));
ct2 .Start();
}
}
}
}
参数this指定应将锁保存在当前对象上。在需要锁定对象,不让其他线程访问它时,应把this指针作为参数和传递。
Wait()和Pulse()机制
Wait()和Pulse()机制用于线程间的交互。当在一个对象上执行Wait()时,正在访问该对象的线程就会进入等待状态。直到它得到一个唤醒信号。Pulse()和PulseAll()用于给等待线程发送信号。
注意,只能在Enter()和Exit()代码块中调用Wait()和Pulse()方法。
using
System;
using System.Threading;
using System.Runtime.CompilerServices;
public class LockMe
{
}
public class WaitPulse1
{
private int result = 0;
private LockMe _1M;
public WaitPulse1() { }
public WaitPulse1(LockMe l)
{
this._1M = l;
}
public void CriticalSection()
{
Monitor.Enter(this._1M);
//Enter the Critical Section
Console.WriteLine("WaitPulse1:Entered Thread" + Thread.CurrentThread.GetHashCode());
for (int i = 1; i <= 5; i++)
{
Monitor.Wait(this._1M);
Console.WriteLine("WaitPulse1:WokeUp");
Console.WriteLine("WaitPulse1:Result=" + result++ + "ThreadID" + Thread.CurrentThread.GetHashCode());
Monitor.Pulse(this._1M);
}
Console.WriteLine("WaitPulse1:Eiting Thread" + Thread.CurrentThread.GetHashCode());
//Exit the Critical Section
Monitor.Exit(this._1M);
}
}
class WaitPulse2
{
private int result = 0;
internal LockMe _1M;
public WaitPulse2() { }
public WaitPulse2(LockMe l)
{
this._1M = l;
}
public void CriticalSection()
{
Monitor.Enter(this._1M);
Console.WriteLine("WaitPulse2:Entered Thread" + Thread.CurrentThread.GetHashCode());
for (int i = 1; i <= 5; i++)
{
Monitor.Pulse(this._1M);
Console.WriteLine("WaitPulse2:Result=" + result++ + "ThreadID" + Thread.CurrentThread.GetHashCode());
Monitor.Wait(this._1M);
Console.WriteLine("WaitPulse2:WokeUp");
}
Console.WriteLine("WaitPulse2:Exiging Thread" + Thread.CurrentThread.GetHashCode());
//Exit the Critical Section
Monitor.Exit(this._1M);
}
}
public class ClassForMain
{
public static void Main(string[] args)
{
LockMe l = new LockMe();
WaitPulse1 e1 = new WaitPulse1(l);
WaitPulse2 e2 = new WaitPulse2(l);
Thread t1 = new Thread(new ThreadStart(e1.CriticalSection));
t1.Start();
Thread t2 = new Thread(new ThreadStart(e2.CriticalSection));
t2.Start();
}
}
在Main()方法中,创建了一个名的l的LockMe的对象。
接着创建两个类型为WaitPulse1和WaitPluse2的对象。注意,WaitPulse1中的LockMe对象实例与WaitPulse2中的LockMe对象实例相同,因为对象按引用被传递给它们各自的构造函数。初始化对象后,创建两个线程t1和t2,把它们分别传递给两个CriticalSection()方法。
假设WaitPulse1.CriticalSection()首先获得调用,则 线程t1通过LockMe对象上的锁定进入该方法的重要代码段,然后在For循环中执行Monitor.Wait()。执行Monitor.Wait()方法后,线程t1等待另一个线程的运行时通知 (Monitor.Pulse()),而被唤醒。锁定LockMe对象是希望在任一时刻只允许一个线程访问共享的LockMe实例。
注意,当线程执行Monitor.Wait()方法时,它会临时释放LockMe对象上的锁,以便其他线程可以访问它。线程t1进入等待状态后,线程t2就可自由访问LockMe对象。即LockMe对象是个独立的对象(WaitPulse1和WaitPuluse2),这两个线程也均指向同一个对象引用。线程t2获得LockMe对象上的锁后,进入WaitPulse2.CriticalSection方法。它一进入For循环,就给在LockMe对象上等待的线程(此时是t1)发送一个运行时通知(Monitor.Pulse()),它后进入睡眠状态。结果t1醒来,获得LckMe对象上的锁,接着线程t1访问result变量,并给在LockMe对象上等待的线程(此时是t2)发送一个运行时通知。这个循环 一直持续到For循环结束。
注意每个Enter()方法都应该伴随一个Exit()方法,否则程序将陷入死循环。
Enter()将一个对象作为其参数。如果该对象参数为null,方法变量或者值类型对象,如整型值,系统就会抛出一个异常。
using System.Threading;
using System.Runtime.CompilerServices;
public class LockMe
{
}
public class WaitPulse1
{
private int result = 0;
private LockMe _1M;
public WaitPulse1() { }
public WaitPulse1(LockMe l)
{
this._1M = l;
}
public void CriticalSection()
{
Monitor.Enter(this._1M);
//Enter the Critical Section
Console.WriteLine("WaitPulse1:Entered Thread" + Thread.CurrentThread.GetHashCode());
for (int i = 1; i <= 5; i++)
{
Monitor.Wait(this._1M);
Console.WriteLine("WaitPulse1:WokeUp");
Console.WriteLine("WaitPulse1:Result=" + result++ + "ThreadID" + Thread.CurrentThread.GetHashCode());
Monitor.Pulse(this._1M);
}
Console.WriteLine("WaitPulse1:Eiting Thread" + Thread.CurrentThread.GetHashCode());
//Exit the Critical Section
Monitor.Exit(this._1M);
}
}
class WaitPulse2
{
private int result = 0;
internal LockMe _1M;
public WaitPulse2() { }
public WaitPulse2(LockMe l)
{
this._1M = l;
}
public void CriticalSection()
{
Monitor.Enter(this._1M);
Console.WriteLine("WaitPulse2:Entered Thread" + Thread.CurrentThread.GetHashCode());
for (int i = 1; i <= 5; i++)
{
Monitor.Pulse(this._1M);
Console.WriteLine("WaitPulse2:Result=" + result++ + "ThreadID" + Thread.CurrentThread.GetHashCode());
Monitor.Wait(this._1M);
Console.WriteLine("WaitPulse2:WokeUp");
}
Console.WriteLine("WaitPulse2:Exiging Thread" + Thread.CurrentThread.GetHashCode());
//Exit the Critical Section
Monitor.Exit(this._1M);
}
}
public class ClassForMain
{
public static void Main(string[] args)
{
LockMe l = new LockMe();
WaitPulse1 e1 = new WaitPulse1(l);
WaitPulse2 e2 = new WaitPulse2(l);
Thread t1 = new Thread(new ThreadStart(e1.CriticalSection));
t1.Start();
Thread t2 = new Thread(new ThreadStart(e2.CriticalSection));
t2.Start();
}
}
在Main()方法中,创建了一个名的l的LockMe的对象。
接着创建两个类型为WaitPulse1和WaitPluse2的对象。注意,WaitPulse1中的LockMe对象实例与WaitPulse2中的LockMe对象实例相同,因为对象按引用被传递给它们各自的构造函数。初始化对象后,创建两个线程t1和t2,把它们分别传递给两个CriticalSection()方法。
假设WaitPulse1.CriticalSection()首先获得调用,则 线程t1通过LockMe对象上的锁定进入该方法的重要代码段,然后在For循环中执行Monitor.Wait()。执行Monitor.Wait()方法后,线程t1等待另一个线程的运行时通知 (Monitor.Pulse()),而被唤醒。锁定LockMe对象是希望在任一时刻只允许一个线程访问共享的LockMe实例。
注意,当线程执行Monitor.Wait()方法时,它会临时释放LockMe对象上的锁,以便其他线程可以访问它。线程t1进入等待状态后,线程t2就可自由访问LockMe对象。即LockMe对象是个独立的对象(WaitPulse1和WaitPuluse2),这两个线程也均指向同一个对象引用。线程t2获得LockMe对象上的锁后,进入WaitPulse2.CriticalSection方法。它一进入For循环,就给在LockMe对象上等待的线程(此时是t1)发送一个运行时通知(Monitor.Pulse()),它后进入睡眠状态。结果t1醒来,获得LckMe对象上的锁,接着线程t1访问result变量,并给在LockMe对象上等待的线程(此时是t2)发送一个运行时通知。这个循环 一直持续到For循环结束。
注意每个Enter()方法都应该伴随一个Exit()方法,否则程序将陷入死循环。
Enter()将一个对象作为其参数。如果该对象参数为null,方法变量或者值类型对象,如整型值,系统就会抛出一个异常。
TryEnter()方法
Monitor类的TryEnter()方法非常类似于Enter()方法,它试图获得对象的独占锁,不过它不会像Enter()方法那样暂停。如果线程成功进入,则TryEnter()方法返回True。
例如ISP的拨号连接,假定有两个应用程序A和B,它们要使用同一个调制解调器拨号连接ISP。目前只有一个可用的调制解调器连接,而且一旦建立起连接,就无法知道连接上的程序会在网上呆多久。假定应用程序A先拨号连接上ISP,过了一段时间后,应用程序B也想拨号连接; 应用程序B可能会无限期地等待下去,因为我们不知道应用程序A要在网上呆多久。在这种情况下,应用程序B就可以使用TryEnter()来确定调制解调器是否已被其他应用程序(本例是A)锁定,而不是无限期地等待下去。
using
System;
using System.Threading;
using System.Runtime.CompilerServices;
public class ClassForMain
{
public class TryEnter
{ //关键代码段
public void CriticalSection()
{
if (Monitor.TryEnter(this, 1000))
{
try
{
Console.WriteLine("Entering...");
Console.WriteLine("Thread ID: " + Thread.CurrentThread.GetHashCode());
for (int i = 1; i <= 5; i++)
{
Thread.Sleep(1000);
Console.WriteLine("Value:"+i + " " + "Thread ID:"+Thread.CurrentThread.GetHashCode() + " ");
}
}
catch (Exception e)
{
Console.WriteLine("{0}", e.Message);
}
finally
{
Monitor.Exit(this);
Console.WriteLine("Exit!");
}
}
else
{
Console.WriteLine("Fail to enter,I'll try later");
}
}
}
//实例化两个线程,以“拨号”方式尝试进入代码区
public static void Main(string[] args)
{
TryEnter a = new TryEnter();
Thread t1=new Thread(new ThreadStart(a.CriticalSection));
Thread t2 = new Thread(new ThreadStart(a.CriticalSection));
t1.Start();
t2.Start();
}
}
using System.Threading;
using System.Runtime.CompilerServices;
public class ClassForMain
{
public class TryEnter
{ //关键代码段
public void CriticalSection()
{
if (Monitor.TryEnter(this, 1000))
{
try
{
Console.WriteLine("Entering...");
Console.WriteLine("Thread ID: " + Thread.CurrentThread.GetHashCode());
for (int i = 1; i <= 5; i++)
{
Thread.Sleep(1000);
Console.WriteLine("Value:"+i + " " + "Thread ID:"+Thread.CurrentThread.GetHashCode() + " ");
}
}
catch (Exception e)
{
Console.WriteLine("{0}", e.Message);
}
finally
{
Monitor.Exit(this);
Console.WriteLine("Exit!");
}
}
else
{
Console.WriteLine("Fail to enter,I'll try later");
}
}
}
//实例化两个线程,以“拨号”方式尝试进入代码区
public static void Main(string[] args)
{
TryEnter a = new TryEnter();
Thread t1=new Thread(new ThreadStart(a.CriticalSection));
Thread t2 = new Thread(new ThreadStart(a.CriticalSection));
t1.Start();
t2.Start();
}
}
LOCK语句
Lock关键字可以用作Monitor方法的一个替换用法。下面例子输出结果与上面一样:
using
System;
using System.Threading;
using System.Runtime.CompilerServices;
public class ClassForMain
{
public class LockWord
{
public void CriticalSection()
{
lock(this)
{
//Enter the Critical Section
Console.WriteLine("Entering...");
Console.WriteLine("Thread ID: " + Thread.CurrentThread.GetHashCode());
for (int i = 1; i <= 5; i++)
{
Console.WriteLine("Value:"+i + " " + "Thread ID:"+Thread.CurrentThread.GetHashCode() + " ");
Thread.Sleep(1000);
}
Console.WriteLine("Exiting Thread"+Thread.CurrentThread.GetHashCode());
}
}
public static void Main(string[] args)
{
LockWord e = new LockWord();
Thread t1=new Thread(new ThreadStart(e.CriticalSection));
t1.Start();
Thread t2 = new Thread(new ThreadStart(e.CriticalSection));
t2.Start();
}
}
}
using System.Threading;
using System.Runtime.CompilerServices;
public class ClassForMain
{
public class LockWord
{
public void CriticalSection()
{
lock(this)
{
//Enter the Critical Section
Console.WriteLine("Entering...");
Console.WriteLine("Thread ID: " + Thread.CurrentThread.GetHashCode());
for (int i = 1; i <= 5; i++)
{
Console.WriteLine("Value:"+i + " " + "Thread ID:"+Thread.CurrentThread.GetHashCode() + " ");
Thread.Sleep(1000);
}
Console.WriteLine("Exiting Thread"+Thread.CurrentThread.GetHashCode());
}
}
public static void Main(string[] args)
{
LockWord e = new LockWord();
Thread t1=new Thread(new ThreadStart(e.CriticalSection));
t1.Start();
Thread t2 = new Thread(new ThreadStart(e.CriticalSection));
t2.Start();
}
}
}