首先,来看下使用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); } } }