宽城网站制作,响应式网站建设报价单,顺的网站建设服务,美橙互联 网站备案拍照引言#xff1a; 其实这部分内容应该是属于专题四#xff0c;因为这篇也是讲关于线程同步的#xff0c;但是由于考虑到用户的阅读习惯问题#xff0c;因为文章太长了#xff0c;很多人不是很愿意看包括我也是这样的#xff0c;同时也有和我说可以把代码弄成折叠的#x… 引言 其实这部分内容应该是属于专题四因为这篇也是讲关于线程同步的但是由于考虑到用户的阅读习惯问题因为文章太长了很多人不是很愿意看包括我也是这样的同时也有和我说可以把代码弄成折叠的这样就不会太长的但是我觉得这样也不怎么便于阅读因为我看别人的博客的时候看到有代码是折叠起来的时候很多时候不愿意去点并且点一下之后同样拉长文章的然后就看到右边的滚动条变小了本以为快看完了(意思快学到知识了)一看滚动条后发现还有好长的内容很看 所以就会给人一种不舒服的感觉吧(如果有和我一样的人的话你肯定懂的是什么感觉的)。所以我把线程同步放到两篇文章里面来说其实放到两篇文章里面也有一定原因的 前面讲的线程同步主要是用户模式的(CLR Via C# 一书中是这么定义的书中说到线程同步分两种一、用户模式构造 二、内核模式构造第一次看的时候不是很理解两个名词是什么意思的我一般理解东西是采用把东西拆分来理解理解拆分的各个部分后再合起来理解内容的现在我对着两个的理解是——用户模式构造对于内核模式构造指的的是构造操作系内核对象我们使用类.net Framework中的类如 AutoResetEvent, Semaphore类的方法来实现线程同步其实内部是调用操作系统的内核对象来实现的线程同步此时就会导致线程从托管代码到为内核代码然而用户模式构造没有调用操作系统内核对象线程只是在用户的托管代码上执行的)对于用户模式构造和内核模式的构造只是我自己的理解的 如果有更好的理解方式可以留言告诉下我 这样我们可以一起讨论和学习了。 目录 一、WaitHandle基类介绍 二、事件(Event)类实现线程同步 三、小结 一、WaitHandle基类介绍 System.Threading命名空间中提供了一个WaitHandle 的抽象基类此类就是包装了一个Windows内核对象的句柄句柄可以理解为标示了对象实例的一个数字具体大家可以查看资料深入理解下的在这里只是提出理解句柄也是很重要的,在.net Framework中提供了从WaitHandle类中派生的类我正是用这些派生类在我们的代码中实现线程同步的。它们的一个继承关系为 WaitHandle EventWaitHandle AutoResetEvent ManualResetEvent Semaphore Mutex 当我们在使用 AutoResetEvent,ManualResetEvent,Semaphore,Mutex这些类的时候用构造函数来实例化这些类的对象时其内部都调用了Win32 CreateEvent或CreateEvent函数或CreateSemaphore或者CreateMutex函数这些函数调用返回的句柄值都保存在WaitHandle基类定义的SafeWaitHandle字段中。 二、事件(Event)类实现线程同步 2.1 AutoResetEvent 自动重置事件 先讲讲AutoresetEvent类的构造函数其定义为 public AutoResetEvent(bool initialState); 构造函数中用一个bool 类型的初始状态来设置AutoResetEvent对象的状态如果要将AutoResetEvent对象的初始状态设置为终止则传入bool值为true若要设置非终止就传入false。 WaitOne方法定义 public virtual bool WaitOne(int millisecondsTimeout);该方法用来阻塞线程当在指定的时间间隔还没有收到一个信号时将返回false。 调用Set方法发信号来释放等待线程。在使用过程中WaitOne方法和Set方法都是成对出现的 一个用于阻塞线程等待信号一个用来释放等待线程(就是说调用set方法来发送一个信号此时WaitOne接受到信号就释放阻塞的线程线程就可以继续运行) 线程通过调用AutoResetEvent的WaitOne方法来等待信号如果AutoResetEvent对象为非终止状态则线程被阻止等到线程调用Set方法来恢复线程执行。如果AutoResetEvent为终止状态时则线程不会被阻止此时AutoResetEvent将立即释放线程并返回为非终止状态指出有线程在使用资源的一种状态。 下面通过通过一个例子来演示下AutoResetEvent的使用 using System; using System.Threading; namespace KenelMode { class Program { // 初始化自动重置事件并把状态设置为非终止状态 // 如果这里把初始状态设置为True时 // 当调用WaitOne方法时就不会阻塞线程,看到的输出结果的时间就是一样的了 // 因为设置为True时表示此时已经为终止状态了。 public static AutoResetEvent autoEvent new AutoResetEvent(false); static void Main(string[] args) { Console.WriteLine(Main Thread Start run at: DateTime.Now.ToLongTimeString()); Thread t new Thread(TestMethod); t.Start(); // 阻塞主线程3秒后 // 调用 Set方法释放线程使线程t可以运行 Thread.Sleep(3000); // Set 方法就是把事件状态设置为终止状态。 autoEvent.Set(); Console.Read(); } public static void TestMethod() { autoEvent.WaitOne(); // 3秒后线程可以运行所以此时显示的时间应该和主线程显示的时间相差3秒 Console.WriteLine(Method Restart run at: DateTime.Now.ToLongTimeString()); } } } 运行结果从运行结果看确实是过了一秒后在TestMethod方法中的语句 上面中用到的是没有带参数的WaitOne方法该方法表示无限制阻塞线程直到收到一个事件为止通过Set方法来发送一个信号同时我们也可以设置堵塞线程的事件当超时时线程将不阻塞直接运行尽管此时没有通过Set来发送一个信号线程照样运行只是WaitOne方法返回的的值不一样。 bool WaitOne(int millisecondsTimeout) 收到信号时返回为True,没收到信号返回为false。 看完下面的代码你可能会形象理解WaitOne(millisecondsTimeout)方法的使用的 using System; using System.Threading; namespace KenelMode { class Program { // 初始化自动重置事件并把状态设置为非终止状态 // 如果这里把初始状态设置为True时 // 当调用WaitOne方法时就不会阻塞线程,看到的输出结果的时间就是一样的了 // 因为设置为True时表示此时已经为终止状态了。 public static AutoResetEvent autoEvent new AutoResetEvent(false); static void Main(string[] args) { Console.WriteLine(Main Thread Start run at: DateTime.Now.ToLongTimeString()); Thread t new Thread(TestMethod); t.Start(); // 阻塞主线程1秒后 // 调用 Set方法释放线程使线程t可以运行 Thread.Sleep(3000); // Set 方法就是把事件状态设置为终止状态。 autoEvent.Set(); Console.Read(); } public static void TestMethod() { if (autoEvent.WaitOne(2000)) { Console.WriteLine(Get Singal to Work); // 3秒后线程可以运行所以此时显示的时间应该和主线程显示的时间相差一秒 Console.WriteLine(Method Restart run at: DateTime.Now.ToLongTimeString()); } else { Console.WriteLine(Time Out to work); Console.WriteLine(Method Restart run at: DateTime.Now.ToLongTimeString()); } } } } 运行结果 同时这里可以把Thread.Sleep(3000)改成Thread.Sleep(1000)的时候就是说AutoResetEvent对象在超时之前就接到信号了 此时WaitOne(2000)放回的值就是True,就得到的是Get Singal to Work, 之间的事件间隔当然也是1秒了在这里结果就不贴了。 2.2 ManualResetEvent(手动重置事件) ManualResetEvent的使用和AutoResetEvent的使用很类似因为他们都是从EventWaitHandle类派生的不过他们还是有点区别: AutoResetEvent 为终止状态时线程调用 WaitOne则线程不会被阻止。AutoResetEvent 将立即释放线程并返回到非终止状态,当再次调用WaitOne状态时线程会被阻止 这里请注意如果AutoResetEvent初始为非终止状态时 调用WaitOne(int millisecondsTimeout)方法后并不会把状态返回为终止状态此时还是非终止的调用WaitOne方法自动改变状态只针对初始状态为终止状态时有效。 然而ManualResetEvent初始状态为终止状态时时调用WaitOne则线程同样不会被阻止但是ManualResetEvent的状态不会发生改变当我再次调用WaitOne方法是一样不会阻止线程需要我们手动终止() 下面通过一段代码来说明两者的区别 using System; using System.Threading; namespace ManualResetEventSample { class Program { // 初始化自动重置事件并把状态设置为终止状态 public static AutoResetEvent autoEvent new AutoResetEvent(true); public static ManualResetEvent autoEvent new ManualResetEvent(true); static void Main(string[] args) { Console.WriteLine(Main Thread Start run at: DateTime.Now.ToLongTimeString()); Thread t new Thread(TestMethod); t.Start(); Console.Read(); } public static void TestMethod() { // 初始状态为终止状态则第一次调用WaitOne方法不会堵塞线程 // 此时运行的时间间隔应该为0秒但是因为是AutoResetEvent对象 // 调用WaitOne方法后立即把状态返回为非终止状态。 autoEvent.WaitOne(); Console.WriteLine(Method start at : DateTime.Now.ToLongTimeString()); // 因为此时AutoRestEvent为非终止状态所以调用WaitOne方法后将阻塞线程1秒这里设置了超时时间 // 所以下面语句的和主线程中语句的时间间隔为1秒 // 当时 ManualResetEvent对象时因为不会自动重置状态 // 所以调用完第一次WaitOne方法后状态仍然为非终止状态,所以再次调用不会阻塞线程所以此时的时间间隔也为0 // 如果没有设置超时时间的话下面这行语句将不会执行 autoEvent.WaitOne(1000); Console.WriteLine(Method start at : DateTime.Now.ToLongTimeString()); } } } 运行结果 如果你把创建事件为手动重置事件ManualResetEvent时得到的运行结果就会下面这样 2.3 跨进程之间同步 内核模式的构造可同步在同一台机器上的不同进程中运行的线程所以我们同样可以使用 AutoResetEvent实现不同进程中运行的线程同步但是此时需要对AutoResetEvent进行命名但是AutoResetEvent只提供带一个参数的构造函数的此时应该如何去实现不同进程中的线程同步的呢 其实是有解决办法的因为AutoResetEvent是继承自EventWaitHandle类的EventWaitHandle类有多个构造函数的 除了之前的方法创建AutoResetEvent对象外 还可以通过EventWaitHandle AutoEvent new EventWaitHandle (false, EventResetMode.Auto);这样的方法来构造AutoResetEvent对象通过 EventWaitHandle autoEvent new EventWaitHandle (false, EventResetMode.Auto,My);方式就可以指定名称了 下面一段代码演示如何实现跨不同进程中的线程同步 using System; using System.Threading; namespace CrossProcess_EventWaitHandle { class Program { public static EventWaitHandle autoEvent new EventWaitHandle(true, EventResetMode.AutoReset, My); static void Main(string[] args) { Console.WriteLine(Main Thread Start run at: DateTime.Now.ToLongTimeString()); Thread t new Thread(TestMethod); // 为了有时间启动另外一个线程 Thread.Sleep(2000); t.Start(); Console.Read(); } public static void TestMethod() { // 进程一显示的时间间隔为2秒 // 进程二中显示的时间间隔为3秒 // 因为进程二中AutoResetEvent的初始状态为非终止的 // 因为在进程一中通过WaitOne方法的调用已经把AutoResetEvent的初始状态返回为非终止状态了 autoEvent.WaitOne(1000); Console.WriteLine(Method start at : DateTime.Now.ToLongTimeString()); } } } 运行结果 三、小结 本来打算在一篇文章里面讲述内核模式构造的写着写着滚动条又变很小了为了大家的阅读我把信号量和互斥体放在后面一篇文章里面讲吧相信后面的内容会很好理解的因为后面两个类的使用和这篇中讲到的使用很类似好歹都是继承WaitHandle类的。 本文转自LearningHard 51CTO博客原文链接http://blog.51cto.com/learninghard/1034792如需转载请自行联系原作者