首先,来看下使用system.threading.monitor对资源进行保护的思路:
即,使用排它锁,当线程A需要访问某一资源时,对其进行加锁,线程A获取到锁以后,任何其他线程如果再次对资源进行访问,则将其放到等待队列中,知道线程A释放锁之后,再将线程从队列中取出。
主要的两个方法:
Enter | Exit |
获取锁 | 释放锁 |
接着是如何利用enter和exit方法实现线程保护的:
使用对象本身作为锁对象
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
//使用monitor解决和上面类似的问题,
//创建一个自定义的类型resource,然后在主线程和worker线程上调用他的record方法
namespace Monitor
{
public class Resource {
public string called;
//定义要访问的资源
public void Record() {
this.called += string.Format("{0}{1}",Thread .CurrentThread .Name ,DateTime .Now .Millisecond);
Console.WriteLine(called);
}
}
class Program
{
private Resource res = new Resource();
static void Main(string[] args)
{
Thread.CurrentThread.Name = "main ";
Program p = new Program();
Thread worker = new Thread(p.ThreadEntry ); //工作线程
worker.Name = "worker";
worker.Start(); //开启工作线程
p.ThreadEntry(); //主线程中调用同样的方法
}
//要同时共享的方法
void ThreadEntry() {
System.Threading .Monitor.Enter(res); //获取锁
res.Record();
System.Threading .Monitor.Exit(res); //释放锁
}
}
}
使用System.Object作为锁对象
Monitor有一个限制,就是只能对引用类型加锁。
如果将上面的Resource类型改为结构型,就会抛出异常
解决的方法是将锁加载其他的引用类型上:
比如system.object。此时,线程不会访问该引用类型的任何属性和方法,该对象的作用仅仅是协调各个线程。加锁思路:之前对于对象的加锁是占有A,操作A,释放A;现在的操作是占有B,操作A,释放B;
namespace 使用system.object作为锁对象
{
//资源
public struct Resource {
public string Called;
public void Record() {
this.Called += string.Format("{0} {1}",Thread .CurrentThread .Name ,DateTime .Now .Millisecond);
Console.WriteLine(Called);
}
}
//
class Program
{
private Resource res = new Resource();//资源
private object lockobj = new object(); //用来加锁的介质
private object lockobj2 = new object();
static void Main(string[] args)
{
#region 只有一个加锁对象
//Thread.CurrentThread.Name = "main ";
//Program p = new Program();
//Thread worker = new Thread(p.ThreadEntry); //创建新线程
//worker.Name = "worker";
//worker.Start(); //开启新线程
//p.ThreadEntry();
#endregion
#region 双对象:不能保证所有线程加锁和释放锁都是针对同一个对象B
Thread.CurrentThread.Name = "main ";
Program p = new Program();
ParameterizedThreadStart ts = new ParameterizedThreadStart(p.ThreadEntry);
Thread worker = new Thread(p.ThreadEntry); //创建新线程
worker.Name = "worker";
//注意下面工作线程和主线程不是针对一个对象进行加锁的。。。运行时会得出他俩一起进入的时间
worker.Start(p.lockobj); //开启新线程
p.ThreadEntry(p.lockobj2);
#endregion
}
void ThreadEntry(object obj) {
Monitor.Enter(obj);
res.Record();
Monitor.Exit(obj);
}
#region 一个对象加锁
//void ThreadEntry()
//{
// Monitor.Enter(lockobj);//获取锁
// res.Record();
// Monitor.Exit(lockobj);//释放锁
//}
#endregion
}
}
使用System.Type作为锁对象
为了改进使用System.Object作为锁对象时,需要单独创建一个对象加锁使用,不够简洁,所以使用Sytem.type作为锁对象。
使用type的好处是:多次调用typeof(type)获取的是同一个对象
namespace 使用system.type作为锁对象
{
class Program
{
static void Main(string[] args)
{
Thread.CurrentThread.Name = "main ";
Program p = new Program();
Thread worker = new Thread(p.ThreadEntry); //创建新工作线程
worker.Name = "worker";
worker.Start(); //开启工作线程
p.ThreadEntry(); //main里面开启工作线程
}
void ThreadEntry() {
Monitor.Enter(typeof(Resource));//获取锁
Resource.Record();
Monitor.Exit(typeof (Resource)); //释放锁
}
}
public static class Resource{
public static string Called;
public static void Record(){
Called += String.Format("{0} {1}",Thread .CurrentThread .Name ,DateTime .Now .Millisecond);
Console.WriteLine(Called);
}
}
}