管程:
线程同步的另一种方法就是使用管程类,它使用独占锁的方式控制线程的同步,只有只有获得独占锁的线程才能访问临界资源。
当一个线程进入临界区时,首先调用Monitor类的Enter()方法,尝试尝试获取临街资源的独占锁,若独占锁已被其他线程占用,就进入等待状态,直到释放独占锁为止。如果独占锁没有被其他线程占用,该线程会获取到独占锁,进入临界区,执行操作临界资源的代码。如果这时恰好有其他线程也来访问临界资源,后来的线程只能进行等待,睡眠在临界资源上。
Monitor会记录所有睡眠在临界资源上的线程,当线程退出临界区时,需要通过调用Monitor类地Pulse()方法唤醒睡眠在临界资源的线程。
Monitor类的部分方法如下:
Monitor.Enter(object obj);//获取临界资源的独占锁,若不成功,则睡眠在临界资源上
Monitor.TryEnter(object obj);//试图获取临街资源的独占锁,若不成功,立即返回
Monitor.Pulse(object obj);//唤醒睡眠在临界资源上的线程
Monitor.PulseAll(object obj);//唤醒睡眠在临界资源上的所有线程
Monitor.Wait(object obj);//释放独占锁并让当前线程睡眠在临界资源上。
Monitor.Exit(object obj);//释放独占锁,退出临界区
具体实现代码如下:
private static char buffer;//缓存
private static object lockForBuffer = new object();//操作对象
void Start() {
string sentence ="江上一笼统,井上黑窟窿。黄狗身上白,白狗身上肿";
Thread Writer = new Thread(delegate ()
{
for (int i = 0; i < sentence.Length; i++)
{
try
{
Monitor.Enter(lockForBuffer);//进入临界区
buffer = sentence[i];//向缓冲区写入数据
Monitor.Pulse(lockForBuffer);//唤醒睡眠在临界资源上的线程
Monitor.Wait(lockForBuffer);//让当前的线程睡眠在临街资源上
}
catch (System.Threading.ThreadInterruptedException)
{
Debug.Log("线程Writer被终止");
}
finally
{
Monitor.Exit(lockForBuffer);//退出临界区
}
}
});
Thread Reader = new Thread(delegate ()
{
for (int i = 0; i < sentence.Length; i++)
{
try
{
Monitor.Enter(lockForBuffer);//进入临界区
char ch = buffer;
Debug.Log(ch);
Monitor.Pulse(lockForBuffer);//唤醒睡眠在临界资源上的线程
Monitor.Wait(lockForBuffer);//让当前的线程睡眠在临街资源上
}
catch (System.Threading.ThreadInterruptedException)
{
Debug.Log("线程Reader被终止");
}
finally
{
Monitor.Exit(lockForBuffer);//退出临界区
}
}
});
Writer.Start();
Reader.Start();
注意:Monitor类只能锁定引用类对象,而不能锁定值类型对象。因为当参数为值类型变量时,每调用一次Monitor.Enter()方法,值类型变量就会进行一次装箱操作,每进行一次装箱操作,就得到一个新的Object型对象,因此,Monitor类的不同操作发生在不同的对象上,从而不能进行同步。
另外我们还可以使用更为简洁的Lock语句,lock语句执行完毕后就会自动执行Monitor.Exit()方法,释放临界资源。
具体代代码如下:
private static char buffer;
private static object lockForBuffer = new object();
void Start() {
string sentence ="江上一笼统,井上黑窟窿。黄狗身上白,白狗身上肿";
Thread Writer = new Thread(delegate ()
{
for (int i = 0; i < sentence.Length; i++)
{
locak(lockForBuffer)
{
buffer = sentence[i];//向缓冲区写入数据
Monitor.Pulse(lockForBuffer);//唤醒睡眠在临界资源上的线程
Monitor.Wait(lockForBuffer);//让当前的线程睡眠在临街资源上
}
catch (System.Threading.ThreadInterruptedException)
{
Debug.Log("线程Writer被终止");
}
finally
{
Monitor.Exit(lockForBuffer);//退出临界区
}
}
});
Thread Reader = new Thread(delegate ()
{
for (int i = 0; i < sentence.Length; i++)
{
locak(lockForBuffer)
{
char ch = buffer;
Debug.Log(ch);
Monitor.Pulse(lockForBuffer);//唤醒睡眠在临界资源上的线程
Monitor.Wait(lockForBuffer);//让当前的线程睡眠在临街资源上
}
catch (System.Threading.ThreadInterruptedException)
{
Debug.Log("线程Reader被终止");
}
finally
{
Monitor.Exit(lockForBuffer);//退出临界区
}
}
});
Writer.Start();
Reader.Start();
说明:当一个线程以独占锁的方式访问资源时,其他线程就不能访问该资源,只有lock语句结束后其他线程才能访问,这保证了资源访问你的正确性。在某种意义上,lock语句的相当于临时禁用了应用程序的多线程功能。一般情况下,当有多个线程对同一个资源进行写操作时,就应该进行同步操作。