[.Net线程处理系列]专题五:线程同步——事件构造

引言:

    其实这部分内容应该是属于专题四,因为这篇也是讲关于线程同步的,但是由于考虑到用户的阅读习惯问题,因为文章太长了,很多人不是很愿意看包括我也是这样的,同时也有和我说可以把代码弄成折叠的,这样就不会太长的,但是我觉得这样也不怎么便于阅读,因为我看别人的博客的时候,看到有代码是折叠起来的时候很多时候不愿意去点,并且点一下之后同样拉长文章的,然后就看到右边的滚动条变小了,本以为快看完了(意思快学到知识了),一看滚动条后发现还有好长的内容很看, 所以就会给人一种不舒服的感觉吧(如果有和我一样的人的话,你肯定懂的是什么感觉的)。所以我把线程同步放到两篇文章里面来说,其实放到两篇文章里面也有一定原因的, 前面讲的线程同步主要是用户模式的(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的使用:

 
  1. using System;  
  2. using System.Threading;  
  3.  
  4. namespace KenelMode  
  5. {  
  6.     class Program  
  7.     {  
  8.         // 初始化自动重置事件,并把状态设置为非终止状态  
  9.         // 如果这里把初始状态设置为True时,  
  10.         // 当调用WaitOne方法时就不会阻塞线程,看到的输出结果的时间就是一样的了  
  11.         // 因为设置为True时,表示此时已经为终止状态了。         
  12.         public static AutoResetEvent autoEvent = new AutoResetEvent(false);  
  13.         static void Main(string[] args)  
  14.         {  
  15.             Console.WriteLine("Main Thread Start run at: " +DateTime.Now.ToLongTimeString());  
  16.             Thread t = new Thread(TestMethod);  
  17.             t.Start();  
  18.  
  19.             // 阻塞主线程3秒后  
  20.             // 调用 Set方法释放线程,使线程t可以运行  
  21.             Thread.Sleep(3000);  
  22.  
  23.             // Set 方法就是把事件状态设置为终止状态。  
  24.             autoEvent.Set();  
  25.             Console.Read();  
  26.         }  
  27.  
  28.         public static void TestMethod()  
  29.         {  
  30.             autoEvent.WaitOne();  
  31.  
  32.             // 3秒后线程可以运行,所以此时显示的时间应该和主线程显示的时间相差3秒  
  33.             Console.WriteLine("Method Restart run at: " + DateTime.Now.ToLongTimeString());  
  34.         }  
  35.     }  

运行结果(从运行结果看确实是过了一秒后在TestMethod方法中的语句):

 上面中用到的是没有带参数的WaitOne方法,该方法表示无限制阻塞线程,直到收到一个事件为止(通过Set方法来发送一个信号),同时我们也可以设置堵塞线程的事件,当超时时,线程将不阻塞直接运行(尽管此时没有通过Set来发送一个信号,线程照样运行,只是WaitOne方法返回的的值不一样)。

bool WaitOne(int millisecondsTimeout) 收到信号时返回为True,没收到信号返回为false。

看完下面的代码你可能会形象理解WaitOne(millisecondsTimeout)方法的使用的:

 
  1. using System;  
  2. using System.Threading;  
  3.  
  4. namespace KenelMode  
  5. {  
  6.     class Program  
  7.     {  
  8.         // 初始化自动重置事件,并把状态设置为非终止状态  
  9.         // 如果这里把初始状态设置为True时,  
  10.         // 当调用WaitOne方法时就不会阻塞线程,看到的输出结果的时间就是一样的了  
  11.         // 因为设置为True时,表示此时已经为终止状态了。         
  12.         public static AutoResetEvent autoEvent = new AutoResetEvent(false);  
  13.         static void Main(string[] args)  
  14.         {  
  15.             Console.WriteLine("Main Thread Start run at: " +DateTime.Now.ToLongTimeString());  
  16.             Thread t = new Thread(TestMethod);  
  17.             t.Start();  
  18.  
  19.             // 阻塞主线程1秒后  
  20.             // 调用 Set方法释放线程,使线程t可以运行  
  21.             Thread.Sleep(3000);  
  22.  
  23.             // Set 方法就是把事件状态设置为终止状态。  
  24.             autoEvent.Set();  
  25.             Console.Read();  
  26.         }  
  27.  
  28.         public static void TestMethod()  
  29.         {  
  30.             if (autoEvent.WaitOne(2000))  
  31.             {  
  32.                 Console.WriteLine("Get Singal to Work");  
  33.                 // 3秒后线程可以运行,所以此时显示的时间应该和主线程显示的时间相差一秒  
  34.                 Console.WriteLine("Method Restart run at: " + DateTime.Now.ToLongTimeString());  
  35.             }  
  36.             else 
  37.             {  
  38.                 Console.WriteLine("Time Out to work");  
  39.                 Console.WriteLine("Method Restart run at: " + DateTime.Now.ToLongTimeString());  
  40.             }  
  41.         }  
  42.     }  
运行结果:

同时这里可以把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方法是一样不会阻止线程),需要我们手动终止()

下面通过一段代码来说明两者的区别:

 
  1. using System;  
  2. using System.Threading;  
  3.  
  4. namespace ManualResetEventSample  
  5. {  
  6.     class Program  
  7.     {  
  8.         // 初始化自动重置事件,并把状态设置为终止状态  
  9.         public static AutoResetEvent autoEvent = new AutoResetEvent(true);  
  10.  
  11.         public static ManualResetEvent autoEvent = new ManualResetEvent(true);  
  12.         static void Main(string[] args)  
  13.         {  
  14.             Console.WriteLine("Main Thread Start run at: " + DateTime.Now.ToLongTimeString());  
  15.             Thread t = new Thread(TestMethod);  
  16.             t.Start();  
  17.             Console.Read();  
  18.         }  
  19.  
  20.         public static void TestMethod()  
  21.         {  
  22.             // 初始状态为终止状态,则第一次调用WaitOne方法不会堵塞线程  
  23.             // 此时运行的时间间隔应该为0秒,但是因为是AutoResetEvent对象  
  24.             // 调用WaitOne方法后立即把状态返回为非终止状态。  
  25.             autoEvent.WaitOne();  
  26.             Console.WriteLine("Method start at : "+ DateTime.Now.ToLongTimeString());  
  27.  
  28.             // 因为此时AutoRestEvent为非终止状态,所以调用WaitOne方法后将阻塞线程1秒,这里设置了超时时间  
  29.             // 所以下面语句的和主线程中语句的时间间隔为1秒  
  30.             // 当时 ManualResetEvent对象时,因为不会自动重置状态  
  31.             // 所以调用完第一次WaitOne方法后状态仍然为非终止状态,所以再次调用不会阻塞线程,所以此时的时间间隔也为0  
  32.             // 如果没有设置超时时间的话,下面这行语句将不会执行  
  33.             autoEvent.WaitOne(1000);  
  34.             Console.WriteLine("Method start at : " + DateTime.Now.ToLongTimeString());        
  35.         }  
  36.     }  
运行结果:

如果你把创建事件为手动重置事件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");方式就可以指定名称了

下面一段代码演示如何实现跨不同进程中的线程同步:

 
  1. using System;  
  2. using System.Threading;  
  3.  
  4. namespace CrossProcess_EventWaitHandle  
  5. {  
  6.     class Program  
  7.     {  
  8.         public static EventWaitHandle autoEvent = new EventWaitHandle(true, EventResetMode.AutoReset, "My");  
  9.         static void Main(string[] args)  
  10.         {  
  11.             Console.WriteLine("Main Thread Start run at: " + DateTime.Now.ToLongTimeString());  
  12.             Thread t = new Thread(TestMethod);  
  13.               
  14.             // 为了有时间启动另外一个线程  
  15.             Thread.Sleep(2000);  
  16.             t.Start();  
  17.             Console.Read();  
  18.         }  
  19.  
  20.         public static void TestMethod()  
  21.         {  
  22.             // 进程一:显示的时间间隔为2秒  
  23.             // 进程二中显示的时间间隔为3秒  
  24.             // 因为进程二中AutoResetEvent的初始状态为非终止的  
  25.             // 因为在进程一中通过WaitOne方法的调用已经把AutoResetEvent的初始状态返回为非终止状态了  
  26.             autoEvent.WaitOne(1000);  
  27.             Console.WriteLine("Method start at : "+ DateTime.Now.ToLongTimeString());  
  28.         }  
  29.     }  
运行结果:

 三、小结

     本来打算在一篇文章里面讲述内核模式构造的,写着写着滚动条又变很小了,为了大家的阅读,我把信号量和互斥体放在后面一篇文章里面讲吧,相信后面的内容会很好理解的,因为后面两个类的使用和这篇中讲到的使用很类似,好歹都是继承WaitHandle类的。





     本文转自LearningHard 51CTO博客,原文链接:http://blog.51cto.com/learninghard/1034792,如需转载请自行联系原作者


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/539972.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

闪屏页面(Splash)开发

业余作品--365安全卫士 ------------------------------------------- 闪屏页面(Splash):app刚启动时的页面 作用: 1、展示公司品牌logo 2、应用初始化。如游戏app第一次启动初始化数据 3、检测新版本。 4、检测程序合法性。如 招商银行app在启动…

matlab2016b ubuntu命令行安装 + matconvnet的安装

0. 下载安装包 下载的文件有Matlab 2016b Linux64 Crack.rar,R2016b_glnxa64_dvd2.iso,R2016b_glnxa64_dvd1.iso。 1. 拷贝安装文件并上传服务器 在Windows下用虚拟光驱打开,并将R2016b_glnxa64_dvd1.iso和R2016b_glnxa64_dvd2.iso所有内容复…

java 责任链模式 链表_责任链模式的实现及源码中应用

01—责任链模式的实现假设一个出差任务的流程需要审批出差行程和出差报销金额。那么,对应两个部门的审核。我们先定义一个出差任务Task类:然后,我们定义一个抽象的处理类Handler,其中具体的处理方法Handle交给子类去实现。然后&am…

php setcookie 过期,php cookie怎么设置过期时间?

PHP中可以使用setcookie()函数设置cookie的过期时间。语法为“setcookie(name,value,expire,path,domain,secure)”;其中expire参数用于指定cookie的有效期,即过期时间戳。setcookie() 函数向客户端发送一个 HTTP cookie。cookie 是由服务器发送到浏览器…

jQuery Mobile动态刷新页面样式

见 百度经验 http://jingyan.baidu.com/article/7f766dafbc18f24101e1d014.html JQM里面当我们更新了某些页面标签(如: listview, radiobuttons, checkboxes, select menus)里的数据时,必须做refresh操作. 为什么必须做refresh操作操作呢?因为JQM在做页面渲染的时候,为了使样…

R-CNN论文翻译

R-CNN论文翻译Rich feature hierarchies for accurate object detection and semantic segmentation用于精确物体定位和语义分割的丰富特征层次结构2017-11-29摘要过去几年,在权威数据集PASCAL上,物体检测的效果已经达到一个稳定水平。效果最好的方法是融…

sass、gulp应用

Sass介绍n CSS 不是一个编程语言,可以用它来开发网页样式,但是没有办法用它进行编程。SASS 的出现,让 CSS 实现了通过代码编程来实现的方式。n SASS 是一种 CSS 开发工具,提供了许多便利的写法,让CSS 的处理实现了可编…

自定义控件SettingItemView

一、效果图 选中&#xff1a;显示自动更新开启不选择&#xff1a;显示自动更新关闭------------在布局文件中的使用方式和android自生的控件一样 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.andro…

php中如何存储多个文本框,php-如何将每个字符的文本框拆分为多个子文本框

我正在设计一个PHP表单,其中包含一些输入字段,如下图所示.如何将输入类型(文本框)拆分为单个字符子文本框,或将文本框拆分为给定字符串的每个字符的多列文本框.以及如何在其中插入数据| JOHN DOE |进入| J | O | H | N | | D | O | E |这样解决方法:的HTML的CSS#text{backgroun…

为什么会有 AOP

为什么80%的码农都做不了架构师&#xff1f;>>> AOP 面向切面的编程。 先上三张图片 三处对数据库进行操作&#xff0c; 但这三处有大量的重复的代码&#xff0c;每次都是获取session&#xff0c;获取mapper&#xff0c;执行&#xff0c; commit&#xff0c;close…

Android应用检查更新下载安装打开

一、效果 低版本1.02检测到新版本1.03 调用android的安装activity页面 安装完成 打开 1.03版本 二、注意 必须使用签名的应用。因为android不管是虚拟机还是真机安装应用都需要签名。 在开发时&#xff0c;我们运行程序时&#xff0c;开发环境ADT会自动给我们加入一个默认的…

lnmp解析php,搭建LNMP,可以解析PHP文件-Go语言中文社区

一、安装 nginx1. yum -y install pcre-devel zlib-devel links 下载相应软件可以使用rpm -qa 软件名 来查看是否安装成功??2.useradd -u 250 -M -s /sbin/nologin nginx 建立程序用户&#xff0c;降低权限3.tar xf nginx-1.6.0.tar.gz -C /usr/src/nginx 解压并指定…

给控件添加小图标

一、效果 二、知识点 三、代码 <TextViewstyle"style/ContentStyle"android:drawableLeft"android:drawable/star_big_on"android:gravity"center"android:text"远程锁屏:#*lockscreen*#" />

加速计算,为何会成为 AI 时代的计算力“新宠”

随着科技的发展&#xff0c;处理大量数据和进行复杂计算的需求越来越高&#xff0c;人工智能、大数据和物联网等领域更是如此&#xff0c;传统的计算方式已经无法满足这些需求。因此&#xff0c;加速计算作为一种现代计算方式&#xff0c;成了必要的手段。加速计算具有前所未有…

背景选择器selector替换按钮默认背景

一、效果 正常状态 获取焦点或按下 按钮的背景图片是.9图&#xff0c;.9图的制作过程&#xff0c;见下面博文 http://blog.csdn.net/zengmingen/article/details/50193245 二、步骤 模仿android自带的按钮控件编写1、找到android自带按钮的样式。D:\ADT\sdk\platforms\andro…

ios 获取控件高度

2019独角兽企业重金招聘Python工程师标准>>> 1.ios 获取控件相对屏幕的位置 需要获取的对象为view1&#xff0c;则该视图相对屏幕的位置可使用下面方法实现&#xff1a; UIWindow * window[[[UIApplication sharedApplication] delegate] window]; CGRect rect[view…

php文件上传前端页面样式,HTML实现美化上传文件样式

这篇文章介绍的内容是HTML实现美化上传文件i样式 &#xff0c;有着一定的参考价值&#xff0c;现在分享给大家&#xff0c;有需要的朋友可以参考一下传统写法上传文件效果如下图所示这个样式调整了很长时间&#xff0c;最后结果都不尽人意。非常规写法上传文件上传给真正的用于…

08-spring学习-annotation配置

利用annotation配置注入关系 为了更好的解释此类存在的意义&#xff0c;下面通过一段习惯性的开发进行问题的描述&#xff0c;例如&#xff1a; 现在有一个IAdminService服务层&#xff0c;这个服务层要调用的是IAdminDAO和IRoleDAO两个数据层操作&#xff0c;于是定义如下&…

Android 6.0 源代码编译实践

前阵子去上海参加 Android 开发面试&#xff0c;被问及了 Android 的基本原理、常用组件背后的实现机制、设计模式等问题&#xff0c;我都回答地不好。面试时&#xff0c;老司机们常常问我对知识点“背后的实现代码有没有看&#xff1f;”。于是我就想着&#xff0c;回来要把 A…

RNN介绍,较易懂

人类并不是每时每刻都从一片空白的大脑开始他们的思考。在你阅读这篇文章时候&#xff0c;你都是基于自己已经拥有的对先前所见词的理解来推断当前词的真实含义。我们不会将所有的东西都全部丢弃&#xff0c;然后用空白的大脑进行思考。我们的思想拥有持久性。 传统的神经网络并…