添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

dos格式的文件转换为unix格式

dos格式的文件,在unix系统中,比如Solaris中,看到的结果是每行结尾多一个^M.    处理方式有两种:    1.vi编辑替换法    在vi编辑器中打开文件,shift+: 进入命令模式,输入以下命令 “%s/^M//g ”,即用空白字符(不是空格哦)替换^M。其中^是ctrl+V打出来的,M是ctrl+M打出来的。    2.工具转换    在unix系统中,一般都有dos2unix和unix2dos之类的工具,使用dos2unix(在Solaris10中是/bin/dos2unix)可以做到将dos格式的文件转换为unix格式的。    dos2unix inputfile outputfile 本文转自leipei博客园博客,原文链接:http://www.cnblogs.com/leipei2352/archive/2011/06/02/2068608.html,如需转载请自行联系原作者

epub格式电子书剖析之一:文档构成

epub格式电子书遵循IDPF推出的OCF规范,OCF规范遵循ZIP压缩技术,即epub电子书本身就是一个ZIP文件,我们将epub格式电子书的后缀.epub修改为.zip后,可以通过解压缩软件(例如winrar、winzip)进行浏览或解压处理。一个未经加密处理的epub电子书以三个部分组成,其文件结构如下图所示: 1、文件:mimetype 每一本epub电子书均包含一个名为mimetype的文件,且内容不变,用以说明epub的文件格式。文件内容如下: application/epub+zip//注释,表示可以用epub工具和ZIP工具打开 2、目录:META-INF 依据OCF规范,META-INF用于存放容器信息,默认情况下(即加密处理),该目录包含一个文件,即container.xml,文件内容如下: <?xml version='1.0' encoding='utf-8'?> <container xmlns="urn:oasis:names:tc:opendocument:xmlns:container" version="1.0"> <rootfiles> <rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml" /> </rootfiles> </container> container.xml的主要功能用于告诉阅读器,电子书的根文件(rootfile)的路径(红色部分)和打开放式,一般来讲,该container.xml文件也不需要作任何修改,除非你改变了根文件的路径和文件名称。 除container.xml文件之外,OCF还规定了以下几个文件: (1)[manifest.xml],文件列表(2)[metadata.xml],元数据(3)[signatures.xml],数字签名(4)[encryption.xml],加密(5)[rights.xml],权限管理对于epub电子书而言,这些文件都是可选的。 3、目录:OEBPS OEBPS目录用于存放OPS文档、OPF文档、CSS文档、NCX文档,如果是制作中文电子书,则还包括ttf文档(即字体文档),OEBPS目录也可以建立相应的子目录,例如建立chapter目录,把各章节的OPS文档放在chapter目录之中。下图一本epub电子书OEBPS文档的实例图: 其中content.opf文件和toc.ncx文件为必需,其它文件可根据电子书的具体情况而定。 content.opf文件见博文OPF剖析 toc.ncx文件见博文toc.ncx剖析 本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/archive/2011/12/13/2286029.html,如需转载请自行联系原作者

Effective C# 原则1:尽可能的使用属性(property),而不是数据成员(field)。

Effective C# 原则1:尽可能的使用属性(property),而不是数据成员(field)。 我们的目标:尽可能编写出运行效率更高,更健壮,更容易维护的C#代码。 原则一:尽可能的使用属性(property),而不是数据成员(field)。 Always use properties instead of accessible data members. 出于以下几点原因,请在设计类时,尽可能的使用属性,而不是成员。 1、.Net对属性的支持远远大于对成员的支持,你可以对属性进行数据绑定,设计时说明等很多数据成员不被支持的内容。看看.net里的属性面板,你会明白的。 2、数据安全性检测; 属性本质上是两个函数,只是因为C#的特殊语法,但我们可以像访问成员一样的访问它。因此我们可以在属性设计上添加更多更灵活的内容,对属性进行管理。其中对属性数据的检测是其中之一。 在对数据检测时,如果发现数据不满足条件,最好以抛出异常的形式来解决,千万不要设置默认值,这是很危险的事情。例如: public string Name{               if(this._Name==null){                      return “NoName”; }else{        return this._Name;        if(value==null){               this._Name = “NoName”; }else if(value.Length<=10){        this._Name = value; }else{        this._Name = value.SubString(0,10); 看上去很不错不是吗?请马上动手修改你的代码吧,上面的代码是很危险的!或者你还不明白,明明对数据进行了安全检测,为什么还是危险的呢?试想这样的情况,有两个实例o1与o2, o1的Name为null,我们做了这样的事:o2.Name = o1.Name; 结果是什么?o2.Name为”NoName”,而在本质上,o1与o2的Name是根本不相等的。这会对后面的程序运行带来很大的麻烦。请以抛出异常的形式来解决数据不满足条件时的问题。 3、线程同步 对实例的属性可以进行线程同步,而与访问者无关。例如: public string Name{               lock(this){ 当然,你完全可以在类的外面进行线程同步,但那样的工作量远比上面的方法大得多。推荐你使用上面的方法进行线程同步,如果要对静态成员同步,请用lock(typeof(MyClass))方法。 4、属性可以是抽象的,而数据成员不能是抽象的,这为我们设计出兼容性更强,扩展性更强的类提供了好的解决方案。 5、属性可以以接口的形式表现。接口里不能定义数据成员,这在一定程度上限制我们的设计。请用属性来解决这个问题吧。 6、基于属性的索引。索引器是C#语法的特殊内容,而索引器正是通过属性来完成的,如果你想为你的类添加一个索引器,除了用属性你还能用什么呢? 7、最后一点,也是比较重要的。不要直接把公共成员转化成属性。 看了上面的说明,是不是觉得应该马上把所有的数据成员都修改成属性了呢?在你修改前一定要阅读下面的内容。 不要直接把公共成员转化成属性。它们在C#的源代码级上的语法虽然是相同的,但在IL上是不同的。即:o1.Name(属性)和o1.Name(成员),虽然C#代码一样,但IL的代码是不一样的。所以,当你准备修改所有的数据成员时,必须重新编译类的代码,同时也要重新编译访问该类实例的所有相关代码。所以,在你设计时不要为了方便,想先用数据成员使用,然后在后期再修改成属性,这不是一个好主意。 最后,不要为因为使用属性而使IL代码多了几行而担心。虽然数据成员在访问上比属性要快一点,但在实际运行时,不仅对人觉得是一样的,对机器而言感觉也像是一样的快,因为我的CPU越来越快了,完成一个函数调用太快了,跟完成一个数据访问一样的快(当然,这个函数得足够小,就跟我们的属性一样),呵呵。 OK,关于使用属性的问题就先说这些。 ================================   /\_/\                         (=^o^=)  Wu.Country@侠缘       (~)@(~)  一辈子,用心做一件事! --------------------------------   Happy Jimmy, keep dreaming!   ================================ 本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/archive/2011/12/21/2295559.html,如需转载请自行联系原作者

HTML5是继HTML4以后的下一代HTML标准规范,它提供了一些新的元素和属性(例如<nav>网站导航块和<footer>)。新型的标签有利于搜索引擎和语义分析,同时更好地帮助小屏幕装置和视障人士使用,除此之外,也提供了一些新的功能,比如视频音频用的<video>和<audio>,总结而言,有如下几大特点: 取消了一些HTML4里过时的元素和属性标记 其中包括纯粹显示效果的标记,如<font>和<center>,它们已经被CSS取代。HTML5 吸取了XHTML2 一些建议,包括一些用来改善文档结构的功能,比如,新的HTML 标签 header, footer, dialog, aside, figure 等的使用,将使内容创作者更加语义地创建文档,之前的开发者在实现这些功能时一般都是使用div。 内容与展示分离 b 和 i 标签依然保留,但它们的意义和之前有所不同,这些标签的意义只是为了将一段文字标识出来,而不是为了为它们设置粗体或斜体式样。u,font,center,strike 这些标签则被完全去掉了。 新增加一些全新的表单输入对象 包括日期,URL,Email 地址,其它的对象则增加了对非拉丁字符的支持。HTML5 还引入了微数据,这一使用机器可以识别的标签标注内容的方法,使语义Web 的处理更为简单。总的来说,这些与结构有关的改进使内容创建者可以创建更干净,更容易管理的网页,这样的网页对搜索引擎,对读屏软件等更为友好。 全新的、更合理的标签 多媒体对象将不再全部绑定在 object 或 embed Tag 中,而是视频有视频的Tag,音频有音频的 Tag。 这个功能将内嵌一个本地的SQL 数据库,以加速交互式搜索,缓存以及索引功能。同时,那些离线Web 程序也将因此获益匪浅。不需要插件的富动画。 Canvas对象 将给浏览器带来直接在上面绘制矢量图的能力,这意味着用户可以脱离Flash 和Silverlight,直接在浏览器中显示图形或动画。 新的API扩展 为HTMLDocument和HTMLElement借口提供了新的API扩展。 HTML5取代Flash和Silverlight 语法(Syntax) 1 文档媒体类型 HTML5定义的HTML语法大部分都兼容于HTML4和XHTML1,但是也有一部分不兼容。大多数的HTML文档都是保存成text/html媒体类型。 HTML5为HTML语法定义了详细的解析规则(包括错误处理),用户必须遵守这些规则将它保存成text/html媒体类型。如下是一个符合HTML语法规范的例子: <!doctype html> <html> <head> <meta charset="UTF-8"> <title>Example document</title> </head> <body> <p>Example paragraph</p> </body> </html> HTML5为HTML语法也定义了一个text/html-sandboxed媒体类型,以便可以host不信任的内容。 其它能够用在HTML5的语法是XML,它兼容于XHTML1。用XML语法的话需要将文档保存成XML媒体类型,并且根据XML的规范需要设置命名空间(namespace)为http://www.w3.org/1999/xhtml。 下面的例子文档符合HTML5里的XML语法规范,需要注意的是XML文档必须保存成XML媒体类型的,例如application/xhtml+xml或者application/xml。 <?xml version="1.0" encoding="UTF-8"?> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Example document</title> </head> <body> <p>Example paragraph</p> </body> </html> 2 Character Encoding HTML5的HTML语法里,有三种形式可以声明字符串的encoding类型: 在传输级别(transport level)上,在HTTP实例的header里设置Content-Type。 在文件的开头设置一个Unicode的Byte Order Mark(BOM),该字符为文件的encoding方式提供了一个签名。 在文档的前1024个byte之前的内容里,使用带有charset属性的meta元素来声明encoding方式。例如:<meta charset="UTF-8">表明该文档是UTF-8格式的。它是替换原有的<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">语法声明,尽管原有的语法依然可用,但在HTML5里不推荐使用。 对于HTML5里的XML语法,依然和以前的XML语法声明式一样的。 3 DOCTYPE HTML5的HTML语法要求文档必须声明DOCTYPE以确保浏览器可以在标准模式下展示页面。这个DOCTYPE没有其它的目的,并且在XML里是可选项,因为XML媒体格式的文档一直就是在标准模式下处理的。 DOCTYPE的声明方式是<!DOCTYPE html>,不区分大小写。HTML的早期版本声明的DOCTYPE需要很长是因为HTML语言是建立在SGML的基础上,所以需要关联引用一个相对应的DTD。HTML5和之前的版本不一样了,仅仅需要声明DOCTYPE就可以告诉文档启用的是HTML5语法标准了,浏览器会为<!DOCTYPE html>做剩余的工作的。 4 MathML和SVG HTML5的HTML语法允许在文档里使用MathML(数学标记语言)和SVG(可伸缩矢量图)元素。例如,一个非常简单的HTML页面包含一个svg元素画出的圆: <!doctype html> <title>SVG in text/html</title> <p> A green circle: <svg> <circle r="50" cx="50" cy="50" fill="green"/> </svg> </p> 更多复杂的组合标记也是支持的,比如使用svg的foreignObject元素你可以嵌套MathML, HTML,或者自身嵌套。 HTML5已经原生支持IRI了,尽管这些IRI只能在UTF-8和UTF-16的文档里使用。 lang属性如果设置的不合法,将会更新为空字符串,以告诉浏览器是一个未知的语言,作用和XML里的xml:lang一样。 本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/archive/2011/12/26/2301697.html,如需转载请自行联系原作者

var mi = typeof(Popup).GetMethod("UpdatePosition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);            mi.Invoke(popusBottom, null);        private void Button_Click(object sender, RoutedEventArgs e)            MessageBox.Show("Test"); <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <style type="text/css"> width:200px; height:100px; background-color:yellow; /* Rotate div */ transform:rotate(30deg); -ms-transform:rotate(30deg); /* IE 9 */ -moz-transform:rotate(30deg); /* Firefox */ -webkit-transform:rotate(30deg); /* Safari and Chrome */ -o-transform:rotate(30deg); /* Opera */ </style> </head> <body> <div>Hello</div> </body> </html> 2、translate(50px,100px); 偏移。(50px距离left, 100px距离top)  3、transform:scale(2,4); 拉伸。 (2为width扩大2倍,4为heigh扩大4倍) 4、skew(30deg,20deg); 30deg为水平上移动30度, 20deg为垂直上移动20度。 本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/archive/2012/01/11/2318945.html,如需转载请自行联系原作者

create if not exists  table student(StuID integer); 2、 创建带有缺省值的数据表: create table if not exists  schoolTable(schID integer default 0, schName varchar default 'hz'); 3、if not exists 使用  如果已经存在表名、视图名和索引名,那么本次创建操作将失败。加上"IF NOT EXISTS"从句,那么本次创建操作将不会有任何影响. create table if not exists studentTest(StuID integer); 4、primary key create table if not exists  studenttable (stuid integer primary key asc); 创建主键 create table if not exists studenttable2 (stuid integer,stuName varchar, primary key(stuid,stuName)); 创建联合主键 4 unique约束 create table if not exists sutTest(stuID integer unique); 创建唯一性约束 5 check约束 create table if not exists sutTest2(stuID integer, ID integer, check(stuID > 0 and ID <0)); 二、表的修改 1、修改表名 alter table sutTest rename to stutest; 2、向表中添加新列 alter table  stuTest add column stuName varchar; 三、表的删除 drop table if exists stuTest 如果某个表被删除了,那么与之相关的索引和触发器也会被随之删除。  四、创建视图 1、创建简单视图 create view if not exists View_Corporate as select * from corporate where corid > 1 2、创建临时视图 create temp view tempView_Corporate as select * from corporate where corid > 1 五、删除视图 drop view if exists View_Corporate; 六、索引的创建 1、该索引基于corporate表的corID字段。 create index cor_index on corporate(corID); 2、该索引基于corporate表的corID,corname字段,,并且指定每个字段的排序规则 create index cor_index2 on corporate(corID asc, corName desc); 3、创建唯一索引 create unique index cor_index3 on corporate(corID asc, corName desc); 七、删除索引 drop index if exists cor_index3; 八、重建索引 reindex;  重建索引用于删除已经存在的索引,同时基于其原有的规则重建该索引。 九、数据分析 analyze; 十、数据清理 vacuum; 本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/archive/2012/02/20/2359009.html,如需转载请自行联系原作者

Win 8中WPF listview与listBox的Drag、Drop操作

Win 8中WPF  listview与listBox的Drag、Drop操作。 基本原理是将listview中的项拖动到listBox中。         <ListView Grid.Column="0" AllowDrop="True" CanDragItems="True"  CanReorderItems="True" DragItemsStarting="ListView_DragItemsStarting" >             <ListView.Resources>                 <Style TargetType="Rectangle">                     <Setter Property="Width" Value="100"/>                     <Setter Property="Height" Value="100"/>                 </Style>                 </ListView.Resources>             <Rectangle Fill="Orange"  Tag="Rect1" Width="100" Height="100"/>             <Rectangle Fill="Orange" Tag="Rect2" Width="120" Height="120"/>             <Rectangle Fill="Orange" Tag="Rect3" Width="140" Height="140"/>             <Rectangle Fill="Orange" Tag="Rect4" Width="160" Height="160"/>         </ListView>         <ListBox x:Name="lb" Grid.Column="1" Margin="10">         <Rectangle  Margin="10" x:Name="droppanel" Opacity="0.01" Visibility="Collapsed" Grid.Column="1" AllowDrop="True" Drop="droppanel_Drop" Fill="Green"/>     </Grid> </UserControl>

Metro Style App开发快速入门 之XML文件读取,修改,保存等操作

Metro Style App 之XML文件读取,修改,保存等操作 采用Win RT的XML API操作XML文件。分别为加载xml文件,选择xml节点,修改xml节点的属性,最后保存xml文件。同样存在着很多异步操作(async,await)。 运行环境请参考上一篇:Metro Style App之文件访问操作示例 下图为Metro style app的API。 接下来就讲一下具体操作吧: /// </summary> private async Task<XmlDocument> LoadXmlFile(string folder, string file)     StorageFolder storageFolder = await Package.Current.InstalledLocation.GetFolderAsync(folder);     storageFile = await  storageFolder.GetFileAsync(file);     XmlLoadSettings loadSettings = new XmlLoadSettings();     loadSettings.ProhibitDtd = false;     loadSettings.ResolveExternals = false;     return await XmlDocument.LoadFromFileAsync(storageFile, loadSettings);  注意: loadSettings.ProhibitDtd = false;   loadSettings.ResolveExternals = false; 安全设置都要成false,否则会有异常。 还有Win 8中文件访问已经发生了一些变化,使用StorageFolder等。 xml文件如下: 下文是对C#对XML的具体操作 如何完成.Net下XML文档的读写操作 C#操作xml SelectNodes,SelectSingleNode总是返回NULL 与 xPath 介绍 C#中用SelectSingleNode方法解析带有多个命名空间的XML文件 以上只是自己的一点学习心得,如果有什么意见和建议,当然自己还在学习研究中。欢迎大家提出,共同学习,一起进步。 留个问题给大家: 当xml文件增加命名空间属性时('http://www.microsoft.com'),如何获得节点的信息; xml文件如: doc.LoadXml(·"<urlset xmlns='http://www.foo.com'><url xmlns='http://www.bar.com'><loc>http://www.far.com</loc></url></urlset>"); Windows.Foundation.Collections.PropertySet ns = new Windows.Foundation.Collections.PropertySet(); ns.Add("foo", "http://www.foo.com"); ns.Add("bar", "http://www.bar.com"); node = doc.SelectSingleNodeNS("/foo:urlset/bar:url/bar:loc", ns); doc.LoadXml(·"<urlset xmlns='http://www.foo.com'><url xmlns='http://www.bar.com'><loc>http://www.far.com</loc></url></urlset>"); node = doc.SelectSingleNodeNS("/foo:urlset/bar:url/bar:loc", "xmlns:foo='http://www.foo.com' xmlns:bar='http://www.bar.com'");

选好 Async 函数的返回类型

在C# 5.0功能之Async一瞥中,简单的介绍了Async CTP的使用,我们一起领略了下一版本的C#可能给我们带来的强大而简单的编写异步执行的代码的方法。 文中提到一个异步方法的返回值有三个选项: Task<T> 什么时候使用哪一种返回类型,是有讲究的。一不小心使用不当,会让代码产生意想不到的结果。为了避免在将同步代码改成异步代码时出现返回类型选择不恰当的情况,给大家介绍ASync选择返回类型的三法则。 (图片来自Bing搜索) (还是申明一下:本文的例子基于Async CTP SP1 Refresh完成。由于 Async还处于CTP阶段,很多东西还在讨论,因此,也许到正式发布的时候,细节还会变动。) 假设有一个学生类,包括学号ID和姓名Name属性: 1: public class Student 2: { 3: public int ID { get; set; } 4: public string Name { get; set; } 6: public override string ToString() 7: { 8: return string.Format("ID: {0}, Name: {1}.", ID, Name); 9: } 10: } 然后,有一组学生记录的StudentRepository: 1: public class StudentRepository 2: { 3: ICollection<Student> storage; 5: public ICollection<Student> GetStudents() 6: { 7: var studentCollection = CreateStudents(); 8: UpdateNameUnknownStudent(studentCollection); 9: return studentCollection; 10: } 12: private ICollection<Student> CreateStudents() 13: { 14: Thread.Sleep(2000); 15: storage = new Collection<Student>() 16: { 17: new Student(){ ID=1 }, 18: new Student(){ ID=2 } 19: }; 20: return storage; 21: } 23: private void UpdateNameUnknownStudent(ICollection<Student> studentCollection) 24: { 25: foreach (var student in studentCollection) 26: { 27: Thread.Sleep(1000); // Someoperation time like db access or so. 28: student.Name = student.Name ?? "Unknown"; 29: } 30: } 31: } 其中,12行的CreateStudents方法模拟了学生对象的创建,它返回一个学生集合; 第23行UpdateNameUnkownStudent方法遍历学生集合,并且,如果学生的姓名为空,将学生姓名设置成“Unknown”。 第5行GetStudents()作为公开的接口,先后调用CreateStudnets和UpdateNameUnknownStudent方法,并将结果返回。 为了模拟现实代码中例如数据库等操作的处理时间,在第14行和第27行分别加了延时。 然后,创建一个WPF UI,调用上面的StudentRepository类,把结果存放到一个Label中。 界面如下: Demo按钮的事件处理代码如下: 1: private void btnDemo_Click(object sender, RoutedEventArgs e) 2: { 3: StudentRepository repository = new StudentRepository(); 4: var studentCollection = repository.GetStudents(); 5: StringBuilder builder = new StringBuilder(studentCollection.Count); 6: foreach (var student in studentCollection) 7: { 8: builder.AppendLine(student.ToString()); 9: } 10: lblResult.Content = builder.ToString(); 11: } 执行代码,虽然代码能够得到正确结果,但是,在点击Demo按钮后,界面出现了几秒钟的死锁。为了提供更好的用户体验,我们决定把学生创建和为空名学生记录添加Unknown的方法改写成异步方法。 (图片来自Bing搜索) 首先看CreateStudents方法,它返回一个ICollection<Student>的集合。 法则一:对于需要返回对象的方法,我们添加async关键字后,改为返回Task<T>,也即,改为: 1: private async Task<ICollection<Student>> CreateStudentsAsync() 2: { 3: await TaskEx.Delay(2000); 4: storage = new Collection<Student>() 5: { 6: new Student(){ ID=1 }, 7: new Student(){ ID=2 } 8: }; 9: return storage; 10: } 1: private async void UpdateNameUnknownStudentAsync(ICollection<Student> studentCollection) 2: { 3: foreach (var student in studentCollection) 4: { 5: await TaskEx.Delay(1000); // Someoperation time like db access or so. 6: student.Name = student.Name ?? "Unknown"; 7: } 8: } 相应的GetStudent方法和Demo按钮也加上async关键字和await等待结果后,重新编译运行。点击Demo按钮。我们发现,界面是不死锁了,但是结果出错了(就说会产生意想不到的结果吧): ID: 1, Name: .  ID: 2, Name: . 显然,UpdateNameUnknownStudentAsync没有运行。让我们来仔细的看一下执行流。 首先,Demo按钮Click事件调用者调用GetStudentAsync,为理清调用层次,我们记btnDemo_Click方法为L1方法: 1: private async void btnDemo_Click(object sender, RoutedEventArgs e) 2: { 3: StudentRepository repository = new StudentRepository(); 4: var studentCollection = await repository.GetStudentsAsync(); 5: StringBuilder builder = new StringBuilder(studentCollection.Count); 6: foreach (var student in studentCollection) 7: { 8: builder.AppendLine(student.ToString()); 9: } 10: lblResult.Content = builder.ToString(); 11: } 当执行到第4行时,遇到await关键字,调用GetStudentsAsync方法(记为L2方法),并且进入等待状态,等待L2结果。 GetStudentAsync(L2)此时代码如下: 1: public async Task<ICollection<Student>> GetStudentsAsync() 2: { 3: var studentCollection = await CreateStudentsAsync(); 4: UpdateNameUnknownStudentAsync(studentCollection); // We have a problem here … 5: return studentCollection; 6: } 当它执行第3行代码,调用CreateStudnetsAsync以后,遇到await关键字,等待CreateStudentAsync的完成。 CretaeStudent方法完成后,L2方法继续执行到调用UpdateNameUnknownStudentAsync方法(记为L3方法): 1: private async void UpdateNameUnknownStudentAsync(ICollection<Student> studentCollection) 2: { 3: foreach (var student in studentCollection) 4: { 5: await TaskEx.Delay(1000); // Someoperation time like db access or so. 6: student.Name = student.Name ?? "Unknown"; 7: } 8: } L3执行到第5行时,遇到了await,当前线程开始等待TaskEx.Delay完成;同时,它会检查它的调用者(L2)是否有代码可以在当前线程上执行。由于在L2中,并没有await L3方法,因此,当L3中await一个结果时,L2所在进程会执行下一个语句:return studentCollection。 L1中的await等到了L2的return,进而进行下一语句……然而,此时此刻,添加Unknown的M3其实还没有完成。前面看到的残缺的结果就这样被显示了出来。 引入这个问题的原因是,由于在有async关键字的情况下,void或者返回Task都不需要在代码中显式的使用return,我们在改写UpdateNameStudentAsync方法时,没有仔细考虑应当使用void还是Task作为返回类型。因此,到底是返回Task还是保持void是一个在刚开始使用Async时经常遇到的问题。 明白了代码执行流以后,判断方法就不难了: 法则二、当一个方法属于触发后不用理会什么时候完成的方法,可以直接使用void,例如事件处理函数(Event Handler); 法则三、当虽然不需要返回结果,但却需要知道是否执行完成的方法时,返回一个Task,例如示例中的UpdateNameUnknownStudnetAsync方法。 在把示例中UpdateNameUnknownStudentAsync方法改成如下形式,并且在GetStudentAsync方法中await它以后,我们便可以得到预期的结果了: 1: private async Task UpdateNameUnknownStudentAsync(ICollection<Student> studentCollection) 2: { 3: foreach (var student in studentCollection) 4: { 5: await TaskEx.Delay(1000); // Someoperation time like db access or so. 6: student.Name = student.Name ?? "Unknown"; 7: } 8: } 2、UserInformation.GetFirstNameAsync() 获得名 3、UserInformation.GetLastNameAsync() 获得姓 4、 StorageFile image = UserInformation.GetAccountPicture(AccountPictureKind.SmallImage) as StorageFile; 获得用户(小)图片 5、 StorageFile image = UserInformation.GetAccountPicture(AccountPictureKind.LargeImage) as StorageFile; 获得用户(大)图片 6、修改账户的图片         SetAccountPictureResult result = await UserInformation.SetAccountPicturesAsync(null, imageFile, null);         if (result == SetAccountPictureResult.Success)         SetAccountPictureResult result = await UserInformation.SetAccountPicturesAsync(null, null, videoFile);         if (result == SetAccountPictureResult.Success)

Android ImageButton android:scaleType

ImageView的属性android:scaleType,即 ImageView.setScaleType(ImageView.ScaleType)。 android:scaleType是控制图片如何 resized/moved来匹对ImageView的size。 ImageView.ScaleType / android:scaleType值的意义区别: CENTER /center  按图片的原来size居中显示,当图片长/宽超过View的长/宽,则截取图片的居中部分显示  CENTER_CROP / centerCrop  按比例扩大图片的size居中显示,使得图片长 (宽)等于或大于View的长(宽)  CENTER_INSIDE / centerInside  将图片的内容完整居中显示,通过按比例缩小或原来的size使得图片长/宽等于或小于View的长/宽 FIT_CENTER / fitCenter  把图片按比例扩大/缩小到View的宽度,居中显示  FIT_END / fitEnd   把图片按比例扩大/缩小到View的宽度,显示在View的下部分位置  FIT_START / fitStart  把图片按比例扩大/缩小到View的宽度,显示在View的上部分位置 FIT_XY / fitXY  把图片 不按比例 扩大/缩小到View的大小显示  MATRIX / matrix 用矩阵来绘制 本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/p/3145812.html,如需转载请自行联系原作者

深刻理解C#的传值调用和传引用调用

传值调用和传引用调用是几乎所有主流语言都会涉及到的问题,下面我谈谈我对C#中传值调用和传引用调用的理解。 1. 一般对C#中传值调用和传引用调用的理解 如果传递的参数是基元类型(int,float等)或结构体(struct),那么就是传值调用。 如果传递的参数是类(class)那么就是传引用调用。 如果传递的参数前有ref或者out关键字,那么就是传引用调用。 验证示例的代码如下: 实验5的运行结果似乎说明即使参数是类(class),只要不加ref,也是传值调用。 下面就引出了我的理解。 2. 没有ref时,即使参数为引用类型(class)时,也可算是一种传值调用 参数为引用类型时,传递的是该引用类型的地址的一份拷贝,“该引用类型的地址的一份拷贝”即为传值调用的“值”。 注意这里说传递的是该引用类型的地址的一份拷贝,而不是引用类型的地址。 下面将用图的形式来说明以上实验3,实验5和实验6中内存的情况。 2.1 首先是实验3 实验3的内存图如下,实参是函数ChangeByClass外的Person_ref对象,形参是函数ChangeByClass内的Person_ref对象。 从图中我们可以看出实参new出来之后就在托管堆上分配了内存,并且在栈上保存了对象的指针。 调用函数ChangeByClass后,由于没有ref参数,所以将栈上的实参p_val拷贝了一份作为形参,注意这里p_val(实参)和p_val(形参)是指向托管堆上的同一地址。 所以说没有ref时,即使参数为引用类型(class)时,也可算是一种传值调用,这里的值就是托管堆中对象的地址(0x1000)。 调用函数ChangeByClass后,通过p_val(形参)修改了name属性的值,由于p_val(实参)和p_val(形参)是指向托管堆上的同一地址,所以函数外的p_val(实参)的name属性也被修改了。 2.2 然后是实验5 上面的实验3从执行结果来看似乎是传引用调用,因为形参的改变导致了实参的改变。 下面的实验5就可以看出,p_val(形参)和p_val(实参)并不是同一个变量,而是p_val(实参)的一个拷贝。 从图中可以看出第一步还是和实验3一样,但是在调用函数ChangeByClassNew后,就不一样了。 函数ChangeByClassNew中,对p_val(形参)重新分配了内存(new操作),使其指向了新的地址(0x1100),如下图: 所以p_val(形参)的name属性改了时候,p_val(实参)的name属性还是没变。 2.3 最后是实验6 我觉得实验6是真正的传引用调用。不废话了,直接上第一个图。 参数中加了ref关键字之后,其实传递的不是托管堆中对象的地址(0x1000),而是栈上p_val(实参)的地址(0x0001)。 所以这里实参和形参都是栈上的同一个东西,没有什么区别了。我觉得这才是真正的传引用调用。 然后调用了函数ChangeByClassRefNew,函数中对p_val(形参)重新分配了内存(new操作),使其指向了新的地址(0x1100)。 由于p_val(形参)就是p_val(实参),所以p_val(形参)的name属性改变后,函数ChangeByClassRefNew外的p_val(实参)的name属性也被改变了。 而原先分配的对象(地址0x1000)其实已经没有被引用了,随时会被GC回收。 3. 结论 如果传递的参数是基元类型(int,float等)或结构体(struct),那么就是传值调用。 如果传递的参数前有ref或者out关键字,那么就是传引用调用。 如果传递的参数是类(class)并且没有ref或out关键字: 如果调用的函数中对参数重新进行了地址分配(new操作),那么执行结果类似传值调用 如果调用的函数中没有对参数重新进行了地址分配,直接就是使用了传递的参数,那么执行结果类似传引用调用

《CLR Via C# 第3版》笔记之(五) - C#中的伪Union类型

一直以为像C#这种内存自动回收的语言,开发人员无法操作其在内存的布局。现在才知道,CLR也提供了相应的接口,让我们可以更细粒度的对代码进行控制。 C#中控制内存布局的Attribute 模拟C#中的Union类型 1. C#中控制内存布局的Attribute 为了控制自己定义的类或结构在内存中的布局,CLR提供了System.Runtime.InteropServices.StructLayoutAtrribute这个Attribute。 这个Attribute的构造器中提供了3种Layout: 1)LayoutKind.Auto : 由CLR自动排列字段 2)LayoutKind.Explicit :让CLR保持你自己的字段布局 3)LayoutKind.Sequential :利用偏移量在内存中显示排列字段 如果不指定StructLayoutAtrribute,CLR会选择它认为最好的布局。 默认情况下,Microsoft C#编译器对于引用类型选择LayoutKind.Auto,对值类型选择LayoutKind.Sequential。 当然,根据自己的需要可以手动修改。 2. 模拟C#中的Union类型 通过指定LayoutKind.Explicit,将结构体中每个字段开始位置的偏移量都指定为0来模拟Union类型。 类型转换构造器是指 通过一种类型(比如Type1)的实例来构造另一种类型(比如Type2)的实例。 一般用于: 1) Type1和Type2之间没有继承关系,但是仍然希望从Type1转换到Type2 2) Type1和Type2是完全不同的两种类型 使用方法如下: using System; namespace cnblog_bowen public class CLRviaCSharp_8 static void Main(string[] args) Type1 t1 = new Type1(10); t1.Show(); // 通过Type1 来构造 Type2 Type2 t2 = new Type2(t1); t2.Show(); Console.ReadKey(true); public class Type1 private Int32 type1_n; public int Type1_n { get { return type1_n; } } public Type1(Int32 n) type1_n = n; public void Show() Console.WriteLine("type1_n = " + type1_n.ToString()); public class Type2 private Int32 type2_n; // 类型转换构造器,根据Type1来构造Type2 public Type2(Type1 t1) type2_n = t1.Type1_n + 10; public void Show() Console.WriteLine("type2_n = " + type2_n.ToString()); 显示结果如下: 2. 显式/隐式的转换操作符 我们可以看出 通过构造器来完成类型的转换并不是很灵活。 其实C#中还提供了显示和隐式的转换方法。 隐式转换的代码如下: using System; namespace cnblog_bowen public class CLRviaCSharp_8 static void Main(string[] args) Type1 t1 = new Type1(10); t1.Show(); // 通过隐式转换将Type1转为Type2 Type2 t2 = t1; t2.Show(); Console.ReadKey(true); public class Type1 private Int32 type1_n; public int Type1_n { get { return type1_n; } } public Type1(Int32 n) type1_n = n; public void Show() Console.WriteLine("type1_n = " + type1_n.ToString()); public class Type2 private Int32 type2_n; // 类型转换构造器,根据Type1来构造Type2 public Type2(Type1 t1) type2_n = t1.Type1_n + 10; // implicit关键字表示是隐式转换 public static implicit operator Type2(Type1 t1) return new Type2(t1); public void Show() Console.WriteLine("type2_n = " + type2_n.ToString()); 显式转换的代码如下: using System; namespace cnblog_bowen public class CLRviaCSharp_8 static void Main(string[] args) Type1 t1 = new Type1(10); t1.Show(); // 通过显式转换将Type1转为Type2 Type2 t2 = (Type2)t1; t2.Show(); Console.ReadKey(true); public class Type1 private Int32 type1_n; public int Type1_n { get { return type1_n; } } public Type1(Int32 n) type1_n = n; public void Show() Console.WriteLine("type1_n = " + type1_n.ToString()); public class Type2 private Int32 type2_n; // 类型转换构造器,根据Type1来构造Type2 public Type2(Type1 t1) type2_n = t1.Type1_n + 10; // explicit关键字表示是显式转换 public static explicit operator Type2(Type1 t1) return new Type2(t1); public void Show() Console.WriteLine("type2_n = " + type2_n.ToString()); 通过显式/隐式转换操作符来进行类型转换时需要注意两点: 1)显式/隐式转换函数必须是public的 2)显式/隐式转换函数必须是static的 为了更好的理解转换操作符和操作符重载方法,强烈建议将System.Decimal类型作为一个典型来研究。 本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/archive/2011/06/30/2094048.html,如需转载请自行联系原作者

1. 本例中事件的场景介绍 为了更好的介绍事件的机制,首先我们构造一个使用事件的场景(是我以前面试时遇到的一个编程题)。 具体场景大概是这样的:某工厂有个设备,当这个设备的温度达到90摄氏度时,触发警报器报警,同时发送短信通知相关工作人员。 当时我就简单的构造3个类:设备(Equipment),警报器(Alert),短信装置(Message)。 传统的实现方法: 1. 警报器类(Alert)中编写一个报警的方法(StartAlert),短信装置类(Message)中编写一个发短信的方法(SendMessage) 2. 在设备类(Equipment)中编写一个温度90度时调用的方法(SimulateTemperature90) 3. 在设备类(Equipment)中引用警报器类(Alert)和短信装置类(Message) 4. 当温度90度时,SimulateTemperature90中会调用Alert.StartAlert方法和Message.SendMessage方法来完成所需功能 基于事件的实现方法: 1. 警报器类(Alert)中编写一个报警的方法(StartAlert),短信装置类(Message)中编写一个发短信的方法(SendMessage) 2. 在设备类(Equipment)中编写一个温度90度时调用的方法(SimulateTemperature90) 3. 在设备类(Equipment)中编写一个事件(Temperature90) 4. 警报器类(Alert)向设备类(Equipment)注册StartAlert,短信装置类(Message)向设备类(Equipment)注册SendMessage 5. 当温度90度时,SimulateTemperature90会触发事件(Temperature90),     此事件会调用已注册的Alert.StartAlert方法和Message.SendMessage方法来完成所需功能 传统的方法缺陷: 1. 设备类(Equipment)关注警报器类(Alert)和短信装置类(Message),当警报器类(Alert)中的方法变更时,     除了要修改警报器类(Alert),还必须修改设备类(Equipment)中调用警报器类(Alert)中方法的地方 2. 增加功能时,比如增加另一个报警装置(Alert2)时,需要修改设备类(Equipment)中报警的地方 基于事件的实现方法可以很好的改善上述情况: 1. 警报器类(Alert)和短信装置类(Message)关注设备类(Equipment),当警报器类(Alert)中的方法变更时,     只需修改警报器类(Alert)中注册事件的地方,将新的事件注册到设备类(Equipment)的事件(Temperature90)中即可 2. 增加功能时,比如增加另一个报警装置(Alert2)时,将新的报警方法注册到事件(Temperature90)中即可 3. 取消警报器类(Alert)和短信装置类(Message)事件时,只需在相应的类中注销事件就行,无需修改设备类(Equipment) 2. 事件的构造 下面以事件的方法来构造上述应用。 1. 编写事件传递的参数,暂时只包含设备名 2. 定义事件成员 3. 定义触发事件的方法 4. 定义模拟触发事件的方法 代码如下: // 编写事件传递的参数,暂时只包含设备名 public class EquipmentEventArgs:EventArgs // 设备名 private readonly string equipName; public string EquipName { get { return equipName; } } public EquipmentEventArgs(string en) equipName = en; public class Equipment // 设备名 private readonly string equipName; public string EquipName { get { return equipName; } } public Equipment(string en) equipName = en; // 定义事件成员 public event EventHandler<EquipmentEventArgs> Temperature90; // 定义触发事件的方法 protected void OnTemperature90(EquipmentEventArgs e) Temperature90(this, e); // 定义模拟触发事件的方法 public void SimulateTemperature90() // 事件参数的初始化 EquipmentEventArgs e = new EquipmentEventArgs(this.EquipName); // 触发事件 OnTemperature90(e); 3. 注册/注销事件 定义警报器类(Alert)和短信装置类(Message),并在其中实现注册/注销事件的方法。 代码如下: // 警报器类 public class Alert // 定义要注册的函数,注意此函数的签名是与 EventHandler<EquipmentEventArgs>一致的 private void StartAlert(Object sender, EquipmentEventArgs e) Console.WriteLine("Equipment: " + e.EquipName + "'s temperature is 90 now!"); // 向Equipment注册事件 public void Register(Equipment equip) equip.Temperature90 += StartAlert; // 向Equipment注销事件 public void UnRegister(Equipment equip) equip.Temperature90 -= StartAlert; // 短信装置类 public class Message // 定义要注册的函数,注意此函数的签名是与 EventHandler<EquipmentEventArgs>一致的 private void SendMessage(Object sender, EquipmentEventArgs e) Console.WriteLine("Equipment: " + e.EquipName + " sends ‘temperature is 90 now!’ to administrator"); // 向Equipment注册事件 public void Register(Equipment equip) equip.Temperature90 += SendMessage; // 向Equipment注销事件 public void UnRegister(Equipment equip) equip.Temperature90 -= SendMessage; 调用上述事件的代码如下: class CLRviaCSharp_12 static void Main(string[] args) Equipment eq = new Equipment("My Equipment"); Alert alert = new Alert(); Message msg = new Message(); // 注册Alert和Message的事件后 Console.WriteLine("=========注册Alert和Message的事件后================="); alert.Register(eq); msg.Register(eq); eq.SimulateTemperature90(); // 注销Alert的事件后 Console.WriteLine(); Console.WriteLine("=========注销Alert的事件后,只有Message事件=========="); alert.UnRegister(eq); eq.SimulateTemperature90(); Console.ReadKey(true); 调用结果如下: 4. 事件在编译器中的实现 在事件的注册/注销时,我们仅仅是简单使用 +=和-=。那么编译其是如何注册/注销事件的呢。 原来编译器在编译时会自动根据我们定义的公共事件(public event EventHandler<EquipmentEventArgs> Temperature90) 生成私有字段Temperature90和事件的add和remove方法。通过ILSpy查看上面编译出的程序集,如下图: 使用 +=和-=时,就是调用编译器生成的add_***和remove_***方法。 下面就是Alert类的Registered和UnRegister方法的IL代码。 事件是引用类型,那么事件在进行注册或者注销时会不会存在线程并发的问题。比如多个线程同时向设备类(Equipment)注册或注销事件时,会不会出现注册或注销不成功的情况呢。 我们进一步观察add_Temperature90的IL代码如下 主要部分就是上图中的红色线框部分,可能有些人不太熟悉IL代码,我将上面的代码翻译成C#大致如下: public void add_Temperature90(EventHandler<EquipmentEventArgs> value) //[0] class [mscorlib]System.EventHandler`1<class cnblog_bowen.EquipmentEventArgs> EventHandler<EquipmentEventArgs> args0; //[1] class [mscorlib]System.EventHandler`1<class cnblog_bowen.EquipmentEventArgs> EventHandler<EquipmentEventArgs> args1; //[2] class [mscorlib]System.EventHandler`1<class cnblog_bowen.EquipmentEventArgs> EventHandler<EquipmentEventArgs> args2; //[3] bool bool args3; // IL_0000 ~ IL_0006 args0 = this.Temperature90; // IL_0007 ~ IL_002d // IL_0007 ~ IL_0008 args1 = args0; // IL_0009 ~ IL_0015 args2 = (EventHandler<EquipmentEventArgs>)System.Delegate.Combine(args1, value); // IL_0016 ~ IL_0023 args0 = System.Threading.Interlocked.CompareExchange<EventHandler<EquipmentEventArgs>>(ref this.Temperature90, args2, args1); // IL_0024 ~ IL_002d if (args0 != args1) args3 = true; args3 = false; while (args3); 从上面代码可以看出,添加新的事件后(IL_0009 ~ IL_0015),IL会继续验证原有的事件是否被其他线程修改过( IL_0016 ~ IL_0023) 所以我们在注册/注销事件时不用担心线程安全的问题。 5. 显式实现事件 从上面的IL代码,我们看出每个事件都会生成相应的私有字段和相应的add_***和remove_***方法。 对于一个有很多事件的类(比如Windows.Forms.Control)来说,将会生成大量的事件代码,而我们在使用时往往只是使用其中很少的一部分事件。 这样使得在创建这些类的时候浪费大量的内存。为了避免这种现象和高效率的存取事件委托,我们可以构造一个字典来维护大量的事件。 Jeffery Richard在《CLR via C#》中给了我们一个很好的例子,为了以后参考方便,摘抄如下: using System; using System.Collections.Generic; using System.Threading; // 这个类的目的是在使用EventSet时,提供 // 多一点的类型安全型和代码可维护性 public sealed class EventKey : Object { } public sealed class EventSet // 私有字典,用于维护 EventKey -> Delegate映射 private readonly Dictionary<EventKey, Delegate> m_events = new Dictionary<EventKey, Delegate>(); // 添加一个EventKey -> Delegate映射(如果EventKey不存在), // 或者将一个委托与一个现在EventKey合并 public void Add(EventKey eventKey, Delegate handler) Monitor.Enter(m_events); Delegate d; m_events.TryGetValue(eventKey, out d); m_events[eventKey] = Delegate.Combine(d, handler); Monitor.Exit(m_events); // 从EventKey(如果它存在)删除一个委托,并且 // 在删除最后一个委托时删除EventKey -> Delegate映射 public void Remove(EventKey eventKey, Delegate handler) Monitor.Enter(m_events); // 调用TryGetValue,确保在尝试从集合中删除一个不存在的EventKey时, // 不会抛出一个异常。 Delegate d; if (m_events.TryGetValue(eventKey, out d)) d = Delegate.Remove(d, handler); // 如果还有委托,就设置新的地址,否则删除EventKey if (d != null) m_events[eventKey] = d; else m_events.Remove(eventKey); Monitor.Exit(m_events); // 为指定的EventKey引发事件 public void Raise(EventKey eventKey, Object sender, EventArgs e) // 如果EventKey不在集合中,不抛出一个异常 Delegate d; Monitor.Enter(m_events); m_events.TryGetValue(eventKey, out d); Monitor.Exit(m_events); if (d != null) // 由于字典可能包含几个不同的委托类型, // 所以无法在编译时构造一个类型安全的委托调用。 // 因此,我调用System.Delegate类型的DynamicInvoke方法 // 以一个对象数组的形式向它传递回调方法的参数。 // 在内部,DynamicInvoke会向调用的回调方法查证参数的类型安全性,并调用方法。 // 如果存在类型不匹配的情况,DynamicInvoke会抛出一个异常。 d.DynamicInvoke(new Object[] { sender, e }); 使用EventSet类的方法也很简单,修改上面的设备类(Equipment)如下: public class Equipment // 设备名 private readonly string equipName; public string EquipName { get { return equipName; } } public Equipment(string en) equipName = en; private readonly EventSet m_events = new EventSet(); // 用于标示事件类型的Key,当有新的事件时,需要再增加一个Key private static readonly EventKey m_eventkey = new EventKey(); // 注册/注销事件 public event EventHandler<EquipmentEventArgs> Temperature90 add { m_events.Add(m_eventkey, value); } remove { m_events.Remove(m_eventkey, value); } // 定义触发事件的方法 protected void OnTemperature90(EquipmentEventArgs e) m_events.Raise(m_eventkey, this, e); // 定义模拟触发事件的方法 public void SimulateTemperature90() // 事件参数的初始化 EquipmentEventArgs e = new EquipmentEventArgs(this.EquipName); // 触发事件 OnTemperature90(e); 其余代码不用修改,编译后可正常运行。 EventSet类是针对事件很多的类来设计的,本例只有一个事件,这样做的优势并不明显。 本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/archive/2011/07/11/2103058.html,如需转载请自行联系原作者

在无聊的时候看到了网上说VC++ MFC 过时了,首先,我肯定能说出这样的话的人本身就是外行。 你只要在WINDOW平台下写程序VC++ MFC 就绝不过时,当然你也可以直接用API写,但是VC++调用 API是最方便的!说VC++ MFC 过时了简直就是个天大的笑话!不信的话你去用PEID查看你电脑C盘下 的可执行文件,包括QQ   PHOTOSHOP FLASH 反恐精英 等等 大型软件包括JAVA的虚拟机在内都 是用什么语言写的,我可以告诉你,都是C++写的!那么你们说VC++ MFC 过时了吗?         我虽然不太懂JAVA,但是大概了解点。不就是说SUN公司用VC++写一个虚拟机,然后封装自己的 类,在自己的类里还是调用API来实现功能的吗?踏踏实实去学一门语言就够你用了,如果你选择VC++ 你绝对不会后悔的! vc过时论pk 只要windows系列操作系统存在就有API存在!只要API存在VC++/MFC就不会过时,楼主醒醒吧, 早就吹说delphi/VB过时,但在好多传统行业经常活跃着VB/delphi的身影!更何况应用更广的VC?!过不过时并不是某人说了算,也不是微软说了算,而是市场说了算,有需求就有存活力。而要windows系统消失谈何容易,要等到哪个猴年马月!因为wondows足于处理日常大量工作,企业主为了节约成本是不会随便更换那些新潮的操作系统的,“实用就行”将更windows系统长期存在,既然这样,那么VB/delphi/VC/MFC就不会过时。     再说.net,其实也不是什么新鲜的高科技,只不过是微软为了与SUN等公司况争推出的商品,.net框架底层同样是调用windows API函数,我们只不过是在微软基于windows API基础上开发出来的平台上做二次开发,而.net框架底层同样是做样和VC++一样的工作。我们用.net做软件用的是微软写好的封装了大量API函数的类库和框架其实没什么高明之处。为什么我们不在APIs基础上开发出自已的平台来?就像SUN公司在windows环境下运行java的平台一样,而不是天天在这里喊这东西过时那东西过时!    如果微软新推出的操作系统微底改变API 接口,那delphi/VB/VC/BCB等将失去开发新操作系统的应用软件的能力,我敢说微软将彻底被对手打败!微软决策者才不会这么傻瓜!     C出来后,中国程序员大炒特炒,汇编过时了!     C++出来后,中国程序员大炒特炒,C过时了!     java出来后,中国程序员大炒特炒,C++过时了!     几年或十几年过后,什么也没过时,反正自已过时了! 什么新的东西一传入到中国都变味了,落后的IT,浮燥的媒体,跟风的程序员,在几家大软件集团的商业炒作中彻底迷失方向,被别人做好的类库和框架牢牢套住!疲于奔命! 本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/archive/2011/11/08/2241355.html,如需转载请自行联系原作者

我们在平时的编码中javascript中经常会用到[return false;]语句来阻止事件的向上传递,其实[return false;]语句包含了2层意思: 阻止触发事件的元素的默认动作(比如说一个link(<a href="http://www/baidu.com"></a>),它的默认动作就是迁移到baidu首页) 阻止触发事件的元素向上传递事件 由于[return false;]包含了2个意思,所以在使用时首先要明确上面的2个阻止是否符合我们的预期。 如果我们在只想阻止元素的默认动作或者只想阻止元素向上传递事件的情况下误用了[return false;]的话, 那么在大量的代码中就很难调试出问题的所在了。 在javascript中其实有相应的函数分别实现上面的2个阻止,分别是: event.preventDefault() 阻止触发事件的元素的默认动作 event.stopPragation()  阻止触发事件的元素向上传递事件 2. 代码演示区别 下面分5种情况用代码演示[return false;], [preventDefault()], [stopPragation()]的区别。 html代码: <!DOCTYPE> <html xml:lang="zh" lang="zh"> <head> <title>test js 事件阻止</title> <meta http-equiv="content-type" content="text/html;charset=utf-8"/> <script type="text/javascript" src="http://code.jquery.com/jquery.js"></script> <script type="text/javascript" src="my.js"></script> <style type="text/css" media="screen"> #parent{ width:300px; height:300px; boder:1px; background-color:red; margin-left:auto; margin-right: auto; #child{ width:200px; height:200px; margin-left:auto; margin-right: auto; boder:1px; background-color:blue; </style> </head> <body> <div id="parent"> <div id="child"> <a href="http://www.baidu.com" id="lnk">go to baidu!</a> </div> </div> </body> </html> 情况一,不使用[return false;], [preventDefault()], [stopPragation()] 默认的my.js的代码如下: // init $(function(){ $("#parent").bind('click', fun_p); $("#child").bind('click', fun_c); $("#lnk").bind('click', fun_a); function fun_p() alert('parent'); function fun_c() alert('child'); function fun_a() alert('link'); 在浏览器中打开html文件,点击其中的link("go to baidu!"),依次执行以下操作: 触发link的click事件alert('link') 向上传递click事件到父元素div(id为child的div),触发此div的click事件alert('child') 继续传递click事件到父元素div(id为parent的div),触发此div的click事件alert('parent') 执行link的默认动作,即迁移到baidu首页 情况二,使用[return false;] my.js中的function fun_a里追加一行[return false;] function fun_a() alert('link'); return false; 在浏览器中打开html文件,点击其中的link("go to baidu!"),依次执行以下操作: 1. 触发link的click事件alert('link') 情况一中的2~4步被阻止了 情况三,使用[preventDefault()] my.js中的function fun_a里追加一行[event.preventDefault()] function fun_a() alert('link'); event.preventDefault(); 在浏览器中打开html文件,点击其中的link("go to baidu!"),依次执行以下操作: 触发link的click事件alert('link') 向上传递click事件到父元素div(id为child的div),触发此div的click事件alert('child') 继续传递click事件到父元素div(id为parent的div),触发此div的click事件alert('parent') link的默认动作被函数[preventDefault()]阻止了 情况四,使用[stopPragation()] my.js中的function fun_a里追加一行[event.stopPragation()] function fun_a() alert('link'); event.stopPropagation(); 在浏览器中打开html文件,点击其中的link("go to baidu!"),依次执行以下操作: 触发link的click事件alert('link') 执行link的默认动作,即迁移到baidu首页 向上传递事件被函数[stopPragation()]阻止了 情况五,同时使用[preventDefault()]和[stopPragation()] my.js中的function fun_a里追加两行[event.preventDefault()]和[event.stopPragation()] function fun_a() alert('link'); event.preventDefault(); event.stopPropagation(); 在浏览器中打开html文件,点击其中的link("go to baidu!"),依次执行以下操作: 触发link的click事件alert('link') 与情况二一样,说明了[return false;]是包含了[preventDefault]和[stopPragation]两个功能的。 3. 建议 建议尽量使用[preventDefault]和[stopPragation]函数, 即使是在确实要使用[return false;]的地方,也可以上面的情况五那样用[preventDefault]和[stopPragation]来代替。 这样可以使代码的含义更加明确,可读性更好。 本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/archive/2013/04/11/3014767.html,如需转载请自行联系原作者

《Linux内核设计与实现》读书笔记(七)- 中断处理

中断处理一般不是纯软件来实现的,需要硬件的支持。通过对中断的学习有助于更深入的了解系统的一些底层原理,特别是驱动程序的开发。 主要内容: 什么是中断 中断相关函数 中断处理机制 中断控制方法 1. 什么是中断 为了提高CPU和外围硬件(硬盘,键盘,鼠标等等)之间协同工作的性能,引入了中断的机制。 没有中断的话,CPU和外围设备之间协同工作可能只有轮询这个方法:CPU定期检查硬件状态,需要处理时就处理,否则就跳过。 当硬件忙碌的时候,CPU很可能会做许多无用功(每次轮询都是跳过不处理)。 中断机制是硬件在需要的时候向CPU发出信号,CPU暂时停止正在进行的工作,来处理硬件请求的一种机制。 2. 中断类型 中断一般分为异步中断(一般由硬件引起)和同步中断(一般由处理器本身引起)。 异步中断:CPU处理中断的时间过长,所以先将硬件复位,使硬件可以继续自己的工作,然后在适当时候处理中断请求中耗时的部分。 举个例子:网卡的工作原理     网卡收到数据包后,向CPU发出中断信号,请求处理接收到的数据包     CPU将收到的数据包拷贝到内存后,即通知网卡继续工作     至于数据包拷贝至内存后的处理会在适当的时候进行 这样做避免了处理数据包时间过长导致网卡接收数据包速度变慢。 同步中断:CPU处理完中断请求的所有工作后才反馈硬件 举个例子:系统异常处理(比如运算中的除0操作)     应用程序出现异常后,需要内核来处理     内核调用相应的异常处理函数来处理异常     处理完后终了应用程序或者给出message 同步中断应该处理能很快完成的一种中断。 3. 中断相关函数 实现一个中断,主要需要知道3个函数: 注册中断的函数 释放中断的函数 中断处理程序的声明 3.1 注册中断的函数     位置:<linux/interrupt.h>  include/linux/interrupt.h 定义如下: * irg - 表示要分配的中断号 * handler - 实际的中断处理程序 * flags - 标志位,表示此中断的具有特性 * name - 中断设备名称的ASCII 表示,这些会被/proc/irq和/proc/interrupts文件使用 * dev - 用于共享中断线,多个中断程序共享一个中断线时(共用一个中断号),依靠dev来区别各个中断程序 * 返回值: * 执行成功:0 * 执行失败:非0 int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char* name, void *dev) 3.2 释放中断的函数 定义比较简单: void free_irq(unsigned int irq, void *dev) 如果不是共享中断线,则直接删除irq对应的中断线。 如果是共享中断线,则判断此中断处理程序是否中断线上的最后一个中断处理程序,     是最后一个中断处理程序 -> 删除中断线和中断处理程序     不是最后一个中断处理程序 -> 删除中断处理程序 3.3 中断处理程序的声明 声明格式如下: * 中断处理程序的声明 * @irp - 中断处理程序(即request_irq()中handler)关联的中断号 * @dev - 与 request_irq()中的dev一样,表示一个设备的结构体 * 返回值: * irqreturn_t - 执行成功:IRQ_HANDLED 执行失败:IRQ_NONE static irqreturn_t intr_handler(int, irq, void *dev) 4. 中断处理机制 中断处理的过程主要涉及3函数: do_IRQ 与体系结构有关,对所接收的中断进行应答 handle_IRQ_event 调用中断线上所有中断处理 ret_from_intr 恢复寄存器,将内核恢复到中断前的状态 处理流程可以参见书中的图,如下: 5. 中断控制方法 常用的中断控制方法见下表: local_irq_disable() 禁止本地中断传递 local_irq_enable() 激活本地中断传递 local_irq_save() 保存本地中断传递的当前状态,然后禁止本地中断传递 local_irq_restore() 恢复本地中断传递到给定的状态 disable_irq() 禁止给定中断线,并确保该函数返回之前在该中断线上没有处理程序在运行 disable_irq_nosync() 禁止给定中断线 enable_irq() 激活给定中断线 irqs_disabled() 如果本地中断传递被禁止,则返回非0;否则返回0 in_interrupt() 如果在中断上下文中,则返回非0;如果在进程上下文中,则返回0 in_irq() 如果当前正在执行中断处理程序,则返回非0;否则返回0 中断处理对处理时间的要求很高,如果一个中断要花费较长时间,那么中断处理一般分为2部分。 上半部只做一些必要的工作后,立即通知硬件继续自己的工作。 中断处理中耗时的部分,也就是下半部的工作,CPU会在适当的时候去完成。 本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/archive/2013/04/19/3030345.html,如需转载请自行联系原作者

虚拟化技术,有时简称为「虚拟化」,最近经常听人说它。但是却不太清楚它的意思。到底虚拟了什么东西?本来是用来干什么的? 有名的虚拟化软件要数 VMware 和 VirtualBox了。这些软件简单来说的话,就是在已有的OS上虚拟一个计算机出来,然后在这台虚拟计算机上安装别的OS。 即,在一台计算机上可以运行多个OS。 虚拟化技术可以提供上述的功能,但是虚拟化技术并不仅仅是「一台计算机上运行多个OS」。也能够「多台计算机上运行一个OS」。 即,通过虚拟化技术,「计算机的台数和运行的OS的个数的比例不再是1:1」了。 根据虚拟化技术,可以很好的调整计算机数和OS数之间的平衡。根据需要,分配给每个OS的计算机数也是可以改变的。 虚拟化的用途之一就是「根据需要,提供合适的计算能力」。 虚拟化的具体用途,我想在下回介绍。 本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/p/3803368.html,如需转载请自行联系原作者

《Windows Azure Platform 系列文章目录》   笔者在之前的文章中介绍的都是使用IDE,也就是Visual Studio,将本地的aspx发布到Azure Web Site上。   在某些情况下,发布人员可能对于IDE不熟悉,那其实可以通过FTP进行发布。在这里笔者简单介绍一下。   1.首先我们登陆Windows Azure Portal,地址是https://manage.windowsazure.com   2.创建一个新的Web Site,如下图:   3.创建完毕后,点击创建成功的website,如下图:   4.页面跳转,点击Dashboard   5.在Dashboard栏目中,点击Set up deployment credentials   6.在弹出的页面中,设置FTP的用户名和密码,如下图:   7.在Dashboard栏目中,可以浏览到FTP的地址和登录名,如下图:   注意:FTP的登录名是包含[DNS]\[UserName],   虽然我在步骤6中设置了user name是leizhang,但是我们在登陆FTP服务器的时候必须使用leiwebsite\leizhang   8.打开Windows资源管理器,地址输入我上图中的FTP Host Name,   用户名为leiwebsite\leizhang   密码为我在步骤6中的密码,如下图:   9.登陆成功后,将本地的项目文件保存到FTP中的Site目录的wwwroot目录下,图略。 本文转自Lei Zhang博客园博客,原文链接:http://www.cnblogs.com/threestone/p/4223198.html,如需转载请自行联系原作者

Windows Azure Cloud Service (40) 使用VS2013的publishSettings文件,发布Cloud Service

《Windows Azure Platform 系列文章目录》   在之前的文档中,笔者已经介绍了如何使用本地证书上传至云端的方式,将本地的Cloud Service发布至云端。   在本章中,笔者将介绍如何使用publishSettings文件,发布Cloud Service。   注意:本文适用于国外的Azure Global和国内世纪互联运维的Azure China。   一.下载publishSetting文件:   国外的Azure Global,请在浏览器中输入地址:http://go.microsoft.com/fwlink/?LinkID=301775   在登陆框中,输入你的用户名和密码   国内世纪互联运维的Azure China,请在浏览器中输入地址:http://go.microsoft.com/fwlink/?LinkID=301776   在登陆框中,输入你的OrgID和密码   将下载的publishsettings保存在本地磁盘,如D盘根目录下   二.发布Cloud Service   1.首先我们以管理员身份,创建一个Cloud Service   2.点击Publish,进行发布,如下图:   3.在弹出的"Pushlish Sign In"里,下拉框里选择"Manage",管理我们的订阅。   4.在Manage Windows Azure Subscription中,选择Import   5.在Import中,导入我们在步骤一中下载的publishsettings文件   6.Import完毕后,选择相应的订阅名称即可。   7.接下来,就可以继续发布我们的Cloud Service了,步骤略。 本文转自Lei Zhang博客园博客,原文链接:http://www.cnblogs.com/threestone/p/4308875.html,如需转载请自行联系原作者

Windows Azure Cloud Service (44) 将Cloud Service加入Virtual Network Subnet,并固定Virtual IP Address(VIP)

《Windows Azure Platform 系列文章目录》   在之前的文章中,笔者已经详细介绍了如何将Virtual Machine加入Virtual Network,并且绑定固定的Private IP和Virtual IP Address (公网IP地址)   Windows Azure Virtual Network (5) 设置Azure Virtual Machine固定Private IP       Windows Azure Virtual Network (6) 设置Azure Virtual Machine固定公网IP (Virtual IP Address, VIP) (1)       Windows Azure Virtual Network (7) 设置Azure Virtual Machine固定公网IP (Virtual IP Address, VIP) (2)   但是以上的功能仅限于IaaS,如果我们需要在PaaS Cloud Service设置Private IP和Virtual IP Address,应该如何设置?   再想象一个场景,当我们前端使用PaaS Cloud Service (Web Server)实现横向扩展,后端使用2台SQL Server Virtual Machine (Always-On)做数据库服务器。   这种混合PaaS+IaaS的场景,如何让PaaS Cloud Service加入Virtual Network,使用内部IP地址与SQL Server VM进行通信呢?   本章内容分为以下三个部分:   一.管理Virtual IP Address(公网IP地址)   二.管理Virtual Network   三.配置Azure Cloud Service   一.管理Virtual IP Address(公网IP地址)   我们可以通过Azure PowerShell来申请Virtual IP Address,具体读者可以参考之前的Blog:   国外使用Azure Global的用户,可以参考:       Windows Azure Virtual Network (6) 设置Azure Virtual Machine固定公网IP (Virtual IP Address, VIP) (1)       Windows Azure Virtual Network (7) 设置Azure Virtual Machine固定公网IP (Virtual IP Address, VIP) (2)   国内使用世纪互联运维的Azure China用户,可以参考:   Azure China (8) 使用Azure PowerShell创建虚拟机,并设置固定Virtual IP Address和Private IP   如果读者用的是百度查询IP地址,经常会发现Azure上海的IP地址经常会显示来自北京,这是由于百度的IP库比较老,请读者注意    这里笔者不想申请新的Reserved IP Address,我们使用命令: Get-AzureReservedIP   可以查看到之前已经申请的Reserved IP Address,如下图:   我们就继续使用上面的LeiTestVMReservedIP,这个地址是191.234.18.61   上图中,InUse状态为False,没有被占用   二.管理Azure Virtual Network   我们创建一个新的Azure Virtual Network,命名为PaaSVNet   增加2个subnet,命名为Web-Subnet和DB-Subnet,如下图:   Web-Subnet IP Rang为10.0.0.0-10.0.0.127   DB-Subnet IP Rang为10.0.0.128-10.0.0.255   三.配置Azure Cloud Service   我们新建一个Cloud Service,项目命名为LeiPaaSReservedIP,只添加WebRole即可,截图略。   1.将Instance Count数量修改为2   2.修改ServiceConfiguration.Cloud.cscfg文件配置,增加以下内容:   上图中的设置中:   (1)Instance Count为2,需要2个Instance   (2)VirtualNetworksSite节点,设置为虚拟机网络名称,PaaSVNet   (3)InstanceAddress节点设置为WebRole1   (4)Subnet设置为步骤二中的子网名称,为Web-Subnet   (5)ReservedIP设置为我们在步骤一中的Reserved IP Name (LeiTestVMReservedIP)   然后我们将我们的Cloud Service部署到Azure平台上,观察Reserved IP Address,已经使用在步骤一的Public IP:191.234.18.61)   然后我们观察虚拟网络PaaSVNet中的Private IP使用情况   Web Role已经加入了Web-Subnet子网     因为将Instance Count设置为2,这2个PaaS Instance占用了10.0.0.4和10.0.0.5这2个IP地址 参考资料: https://msdn.microsoft.com/library/azure/jj156091.aspx 本文转自Lei Zhang博客园博客,原文链接:http://www.cnblogs.com/threestone/p/4360587.html,如需转载请自行联系原作者

Windows Azure Traffic Manager (6) 使用Traffic Manager,实现本地应用+云端应用的高可用

《Windows Azure Platform 系列文章目录》   注意:本文介绍的是使用国内由世纪互联运维的Azure China服务。   以前的Traffic Manager,背后的Service Endpoint必须是Azure数据中心的Cloud Service。   现在最新的Traffic Manager,Endpoint不仅仅支持Azure数据中心的Cloud Service和Web Site。同时还支持部署在自建数据中心(第三方托管)的应用程序。   这种增强功能对客户的收益在于,平时客户可以访问部署在Azure数据中心的应用。如果Azure数据中心发生问题,可以切换到用户部署在自建数据中心的应用程序。实现高可用。   注意:如果需要添加自建数据中心(第三方托管)的应用程序,必须使用Azure PowerShell命令行工具。   模拟场景:假设某公司有2个托管网站   -  一个部署在Azure数据中心,DNS为LeiTestWinVM.chinacloudapp.cn   -  另外一个托管在自建数据中心,DNS为www.sjtu.edu.cn (咳咳,只能使用母校的域名了)   -  我们需要使用Azure PowerShell创建Traffic Manager,负载均衡器规则设置为Failover(故障转移)。同时使用Azure PowerShell增加以上2个服务的DNS节点。   有关Azure PowerShell的内容笔者不想重复了,不熟悉的读者可以参考笔者之前的文档:   Azure PowerShell (1) PowerShell入门   1.我们执行以下命令: $profile = New-AzureTrafficManagerProfile -Name "LeiTrafficManager" -DomainName "LeiTrafficManager.trafficmanager.cn" -LoadBalancingMethod "Failover" -Ttl 30 -MonitorProtocol "Http" -MonitorPort 80 -MonitorRelativePath "/" $profile = Add-AzureTrafficManagerEndpoint -TrafficManagerProfile $profile -DomainName "LeiTestWinVM.chinacloudapp.cn" -Status "Enabled" -Type "CloudService" $profile = Add-AzureTrafficManagerEndpoint -TrafficManagerProfile $profile -DomainName "www.sjtu.edu.cn" -Status "Enabled" -Type "Any" Set-AzureTrafficManagerProfile –TrafficManagerProfile $profile   上面的命令行中:   -  第一行命令,创建了新的Traffic Manager,并且命名为LeiTrafficManager,定义了负载均衡器规则设置为Failover(故障转移),监控的协议为HTTP,同时设置了TTL和监控的端口80   -  第二行命令,对Traffic Manager增加Azure云端的DNS 服务:LeiTestWinVM.chinacloudapp.cn (为一台Azure虚拟机)   -  第三行命令,对Traffic Manager增加本地托管的DNS服务:www.sjtu.edu.cn   -  第四行命令,创建该Traffic Manager   2.执行结果如下:   3.查看Azure Management Portal的配置页面   上图中,配置页面中的内容,就是笔者在步骤一的配置。   注意上图中红色部分,故障转移优先级列表:   -  如果2个云服务都是联机状态。用户优先访问Azure云上的服务, DNS为:LeiTrafficManager.trafficmanager.cn   -  如果Azure云上的服务发生异常宕机了。用户访问列表上的第2个地址,DNS为:www.sjtu.edu.cn   4.当Azure Traffic Manager配置的DNS都是联机状态下:   访问Traffic Manager,会优先访问到第一个DNS服务器地址。截图如下:   5.如果我们模拟第一个DNS服务器发生宕机的情况。比如笔者将Azure VM关闭。观察到Azure VM所在的DNS状态为已降级。如下图:   6.这时候我们再访问Traffic Manager,会优先访问到第二个DNS服务器地址。截图如下:   这样,就实现了横跨本地应用+云端应用的高可用。   7.最后请读者别忘记了,将自己的域名增加CName,指向Traffic Manager所在的DNS服务。 本文转自Lei Zhang博客园博客,原文链接:http://www.cnblogs.com/threestone/p/4527279.html,如需转载请自行联系原作者

Windows Azure Cloud Service (12) PaaS之Web Role, Worker Role, Azure Storage Queue(下)

《Windows Azure Platform 系列文章目录》   本章DEMO部分源代码,请在这里下载。   在上一章中,笔者介绍了我们可以使用Azure PaaS的Web Role和Worker Role来处理复杂的业务逻辑   -Web Role可以快速响应前端的业务请求,并将输入保存到Azure Storage Queue中   -Worker Role将数据从Queue中读取,可以在后端处理复杂的业务逻辑   -可以看到,Azure Storage Queue是前端业务逻辑和后端业务处理的桥梁   该架构图可以用下图表示:   有关Azure Storage Queue的知识,可以参考Windows Azure Storage (1) Windows Azure Storage Service存储服务   接下来,我们模拟一个场景:   1.前端用户通过Web Role的Asp.NET页面,将输入框的内容增加到Azure Storage Queue中   2.后端的Worker Role,通过WorkerRole.cs中的Run()函数,从Azure Storage Queue中拿到消息内容,进行输入。处理完毕后,将该消息删除。   注意:本章内容中,Web Role只响应前端的页面请求。Worker Role在后端处理复杂的业务处理。   Web Role和Worker Role是计算分离的(注意是计算分离,不是多线程)。   因为Web Role和Worker Role是部署在不同的计算节点上。不会因为用户访问Web Role,造成CPU压力过高而影响Worker Role。   以下是源代码讲解部分:   1.首先,我们创建一个新的cloud project,重命名为AzureWorkerRole。图略:   2.在项目文件中,添加Web Role和Worker Role。如下图:   模板我们选择Web Form。图略。   3.在WebRole1中,增加Default.aspx页面,添加TextBox和Button控件。增加以下代码: using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using Microsoft.WindowsAzure.ServiceRuntime; using Microsoft.WindowsAzure.Storage; using Microsoft.WindowsAzure.Storage.Queue; namespace WebRole1 public partial class Default : System.Web.UI.Page protected void Page_Load(object sender, EventArgs e) protected void btnSubmit_Click(object sender, EventArgs e) AddMessage(txbInput.Text.Trim()); txbInput.Text = ""; /// <summary> /// 将消息加入到Azure Storage Queue /// </summary> /// <param name="inputMessage"></param> private void AddMessage(string inputMessage) var account = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("StorageConnection")); var queueClient = account.CreateCloudQueueClient(); var queue = queueClient.GetQueueReference("taskqueue"); queue.CreateIfNotExists(); CloudQueueMessage message = new CloudQueueMessage(inputMessage); queue.AddMessage(message);   核心代码为queue.AddMessage()。将消息内容增加到Azure Storage Queue中。   4.在WorkerRole.cs增加以下代码: /// <summary> /// Editor: Lei Zhang /// Create Azure Storage Queue /// </summary> private void CreateAzureStorageQueue() var account = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("StorageConnection")); var queueClient = account.CreateCloudQueueClient(); //Azure Storage Queue名称必须为小写 var queue = queueClient.GetQueueReference("taskqueue"); queue.CreateIfNotExists(); /// <summary> /// 从Azure Storage Queue中读取数据 /// </summary> private void GetQueue() var account = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("StorageConnection")); var queueClient = account.CreateCloudQueueClient(); //Azure Storage Queue名称必须为小写 var queue = queueClient.GetQueueReference("taskqueue"); queue.CreateIfNotExists(); // dequeue the message and lock message in 30 seconds CloudQueueMessage retrievedMessage = queue.GetMessage(TimeSpan.FromSeconds(30)); if (retrievedMessage == null) return; Trace.TraceInformation("Retrieved message with content '{0}'", retrievedMessage.AsString); // Async delete the message queue.DeleteMessage(retrievedMessage); Trace.TraceInformation("Deleted message");   Worker Role的核心代码为上面的queue.GetMessage(TimeSpan.FromSeconds(30))和 queue.DeleteMessage()。   当有多个Worker Role的情况下,某个Worker Role Instance使用queue.GetMessage(TimeSpan.FromSeconds(30))读取到Queue Message的时候,默认会在这个消息上加一个锁,时间间隔为30秒。   在30秒内,其他Worker Role Instance不会读取到这个Message,以防止Message被重复读取。   Message被读取到并处理完毕后,记得用DeleteMessage删除该消息   我们还可以通过以下API,批量读取20条消息(Queue Message),最多读取32个消息。同时将读取每一条消息的锁设置为5分钟 foreach (CloudQueueMessage message in queue.GetMessages(20, TimeSpan.FromMinutes(5))) // Process all messages in less than 5 minutes, deleting each message after processing. queue.DeleteMessage(message);   5.最后我们在Web Role和Worker Role的Settings,增加相应的Azure Storage Connection String连接字符串。   6.我们在本地,通过Visual Studio 2013运行程序。在Default.aspx页面中输入消息内容,如下:   7.然后我们打开本地模拟器,可以看到Worker Role的输出。   8.我们重复在Default.aspx页面中输入多个值。在本地模拟器,可以看到Worker Role的多个输出。   9.我们可以在WorkerRole.cs的代码中,还可以异步处理其他复杂的业务逻辑,比如异步发送邮件,异步处理数据等等。 本文转自Lei Zhang博客园博客,原文链接:http://www.cnblogs.com/threestone/p/4201065.html,如需转载请自行联系原作者

Windows Azure Service Bus (4) Service Bus Queue和Storage Queue的区别

《Windows Azure Platform 系列文章目录》   熟悉笔者文章的读者都了解,Azure提供两种不同方式的Queue消息队列:   1.Azure Storage Queue   具体可以参考:       Windows Azure Cloud Service (12) PaaS之Web Role, Worker Role, Azure Storage Queue(下)   Azure Storage Queue提供基础的消息队列服务,例如AddMessage, DeleteMessage。   Azure Storage Queue消息容量为64KB(使用 Base64 编码时为 48 KB),最大容量为200TB   Azure Storage Queue的消息接受,需要在WorkerRole.cs的Run()函数中编写额外的代码。   2.Azure Service Bus Queue   具体可以参考:       Windows Azure Service Bus (2) 队列(Queue)入门       Windows Azure Service Bus (3) 队列(Queue) 使用VS2013开发Service Bus Queue       Windows Azure Service Bus (5) 主题(Topic) 使用VS2013开发Service Bus Topic   Azure Service Bus Queue提供更加复杂的消息队列服务,例如队列、主题、中继等。   Azure Service Bus Queue的消息容量为256KB,最大容量1GB至80GB,能够保证绝对的First-In-First-Out (FIFO)   Azure Service Bus Queue的消息接受,在WorkerRoleWithSBQueue1的Run()函数中,提供Client.OnMessage((receivedMessage) => ,用来接收消息。   其他有关Azure  Storage Queue和 Service Bus Queue的详细比较,请参考MSDN文章:   https://msdn.microsoft.com/zh-cn/library/azure/hh767287.aspx 本文转自Lei Zhang博客园博客,原文链接:http://www.cnblogs.com/threestone/p/4533641.html,如需转载请自行联系原作者

C# HashTable的用法总结

HashTable: 表示键/值对的集合,这些键/值对根据键的哈希代码进行组织。Hashtable中keyvalue键值对均为object类型,所以Hashtable可以支持任何类型的keyvalue键值对. 对哈希表的操作包括 1、添加keyvalue键值对 2、遍历哈希表 3、对哈希表进行排序 4、判断哈希表是否包含特定键,其返回值为true或false 5、移除一个keyvalue键值对 6、输出哈希表中的值 7、移除所有元素 $("p").hide() - 隐藏所有段落 $("p.test").hide() - 隐藏所有 class="test" 的段落 $("#test").hide() - 隐藏所有 id="test" 的元素 1、$(this).hide() 演示 jQuery hide() 函数,隐藏当前的 HTML 元素。 在我的代码旅程和编程经历中,已经遭遇很多奇特的对手,还有更为奇特的盟友。我至少发现有5种代码勇士,有些是出色的战友,其他则似乎都在搅黄我的每个计划。 不过他们都在软件开发的万神殿上都有一席之地。如果没有不同编程风格的良好组合,你可能会发现你的项目要不就是耗时过长,要不就是太不稳定或太过完美而无人去做。 1. The duct tape programmer 补漏灵型 The code may not be pretty, but damnit, it works! 代码或许不漂亮,但能用! 这种家伙是公司的基石。代码有问题的时候,他能快速补漏,下不再犯。当然他并不关注代码看起来怎么样,是否容易使用,以及其他琐碎的问题,但是他能搞定,没有一堆废话,也不会唧唧歪歪。用这种程序员的最佳方式是,你指出问题后,走开就可以了。 2. The OCD perfectionist programmer 完美主义强迫症型 You want to do what to my code? 你想对我的代码做什么? 这类家伙并不关心你的截止日期或预算,和编程艺术相比,那些都微不足道。当你最终收到最终成品时,你没有选择余地,只能对那漂亮格式的代码,不,是白玉无瑕的代码,衷心地赞叹。那代码是如此有效率,你无须再对它做什么,否则就是画蛇添足,亵渎大师手笔。他就是那位唯一有资格改动代码的人。 3. The anti-programming programmer 反编程型I’m a programmer, damnit. I don’t write code.  我是一个程序员,擦!我不写代码。 他的世界只有一个简单的真理:写代码不好。如果你不得不写些东西,那你就错了。因为早已有人做过了,只需拿来就是了。即便他写代码所用时间和其他程序员差不多,或更长,他会告诉你,这种开发实践是多么多么地快。当你拿到他提交的项目时,可能实际代码只有20行,也易于阅读。代码或许并不快、高效或向前兼容,但所耗费之力是最小的。 4. The half-assed programmer 得过且过型What do you want? It works doesn’t it? 你想要什么呢?代码不是能用么? 这种家伙很少关注代码质量,那是其他人的工作。他只快速完成安排的任务。你可能不喜欢他的代码,其他程序员也讨厌,但管理层和客户喜欢。虽然将来他会给你带来很多的痛苦,但他可以单枪匹马地在期限之前干完活,所以你不能嘲笑他(不管你心里有多想)。 5. The theoretical programmer 高谈阔论型Well, that’s a possibility, but in practice this might be a better alternative. 嗯,有那种可能,但实践中这种应该更好。 这家伙对应该要做事之外的份外事更感兴趣。他80%的时间在呆呆地盯着电脑,15%的时间在抱怨不合理的期限,4%的时间在琢磨份外事,1%的时间在写代码。当你拿到他的最终成品时,总会看到这样的话,“要是时间多的话,这个地方我可以做得更好。” 你是哪一种? 个人而言,我自认为是完美主义者。所以,你是哪种类型呢?或者是哪几种的混合型呢?还是你觉得还有上面没有讲到的类型?请在评论留言。 编注:看完此文后,推荐再看看 Jeff Atwood 的《程序员的8种级别》一文。 本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/archive/2012/02/01/2334521.html,如需转载请自行联系原作者

运用JavaScript构建你的第一个Metro式应用程序(onWindows 8)(二)

先前的学习中,我们已经了解了 Metro式的 JavaScript 应用程序大致如何,以及通过使用 Microsoft Visual Studio 11 Express for Windows Developer Preview 搭建你的应用程序。接下来要探讨的是,把  Windows 平台的所提供的功能,呈现在您眼前,为扩展您的程序而准备! In the previous topic, you learned about Metro style app usingJavaScript, and used Microsoft Visual Studio 11 Express for WindowsDeveloper Preview to create a framework for your first app. Now it'stime to extend your application using functionality exposed by theWindows platform. 该文是《运用 JavaScript 构建你的第一个 Metro式应用程序(on Windows8)的第二篇,如果你尚未阅读第一篇,请立刻翻阅。如果读者已经熟悉 HTML5、CSS3 和 JavaScript最好,不熟悉也没关系,不影响理解主体意思。 This topic is the second in a series describing how to build yourfirst Windows Metro style app using JavaScriptMetro style app usingJavaScript. If you haven't already done so, read Building your firstWindows Metro style app before you read this topic. Familiarity withHTML5, Cascading Style Sheets, Level 3 (CSS3), and JavaScript ishelpful, but not essential, to understanding this topic. 加入依赖条件Capabilities 当用户从 Windows Store 处安装程序的时候,Windows 会对应用程序的元数据作分析,认定那些依赖条件(Capabilities)。例如,要求访问互联网、访问文档、图片、播放音频视频的时候,又或者访问如摄像头、麦克风等的硬件时候,必须先告知用户,我这个程序需要些以上何种何种的条件才能运行。要用户首肯才能使用这些资源或设备。反之,如果没有列出的那些资源或设备,则一定不是依赖的条件,而且一定不能够运行的(译注:意思即是未列出的就不运行)。程序所需要的依赖条件列明在程序清单(manifest)中,具体说,就是你可以双击“Visual  Studio Solution Explorer ”里面的 package.appxmanifest 文件,点击 “Capabilities” 标签页就可以选择你需要的依赖条件。默认下,你的项目都是设定有最常见的依赖条件,具备充当 Internet 客户端的条件。 When a user installs an app from the Windows Store, Windows use theapp's metadata to figure out what capabilities it needs to run. Forexample, accessing data from the internet, accessing documents,pictures, music or videos or getting access to hardware like themicrophone or webcam. At install time, the user sees whichcapabilities an app asks for, so the app is installed only if theuser agrees for it to access those resources. If the app hasn'tlisted itself as needing access to a certain kind of resource, itisn't allowed access at run time. An app lists the capabilities itneeds in the manifest, so you can double-click on thepackage.appxmanifest file in the Visual Studio Solution Explorer,click the Capabilities tab and select the ones you need. By default,your project has the most common capability enabled, the ability toact as a client for internet service. Windows Runtime运行时 目前为止,程序运行起来,仍属于浏览器技术的一部分内容,至今还未真正接触到 Windows 平台的运行时,特定称谓 “WindowsRuntime”,简称 “WinRT”。而 WinJS,就是允许在 JavaScript Metro 式的应用程序中,借此对象可以访问到 Windows Runtime 类库里面许许多多的功能。通过这个命名空间你还可以通过 Metr app 文档区翻阅更详尽功能。比如现在我们所使用的 Windows.Web.Syndication,用它直接获取已解析的 RSSReed,好处是不用在处理 RSS 或 ATOM 的之间上考虑怎么用 Xpath 的问题而浪费时间。 So far, we haven't really used the Windows platform on which ourapp is running, instead we used the web technologies that are alsopart of the browser. But in addition to WinJS, a Metro style appusing JavaScript can access a large amount of functionality in theWindows Runtime, broken up into namespaces that you can explore inthe Metro style app reference section. The namespace we want isWindows.Web.Syndication, because it has the SyndicationClient classthat we can use to retrieve a fully-parsed RSS feed. This saves usthe trouble of XPath or worrying about the difference between RSS andATOM. 无须太多语句或者项目引用,Metro App 下的 WinRT 元素就直接可用,这样使用 SyndicationClient 非常地简单。 The elements of the WinRT are available to Metro style apps usingJavaScript without any additional script includes or projectreferences, so using the SyndicationClient is simple: [javascript] view plaincopy WinJS.Application.onmainwindowactivated = function (e) {       if (e.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {           // 开始下载           downloadStatus.innerText = "downloading posts...";           //  用WinRT 下载 RSS           var syn = new Windows.Web.Syndication.SyndicationClient();           var url = new Windows.Foundation.Uri("http://blogs.msdn.com/b/oldnewthing/rss.aspx");           syn.retrieveFeedAsync(url).then(processPosts, downloadError);   创建 SyndicationClient 对象之后接着创建一个 URL对象,并将其送入 RetrieveFeedAsync 进行下载。就像其他数量庞大的 Windows Runtime 函数那样,RetrieveFeedAsync 是异步的,自然也会产生“允诺Promise”。这样处理 RSS 的话更简单一些了: After creating the SyndicationClient object, we create a URLobject and feed it to the RetrieveFeedAsync method to start thedownload. Like a large number of Windows Runtime functions,RetrieveFeedAsync is asynchronous and therefore produces a promise.Processing the RSS becomes a little bit easier now:   // 循环条目     for (var i = 0, len = feed.items.length; i < len; i++) {       var item = feed.items[i];       // 向 #posts div 添加数据       var parent = document.createElement("div");       appendDiv(parent, item.title.text, "postTitle");       appendDiv(parent, item.publishedDate, "postDate");       appendDiv(parent, item.summary.text, "postContent");       posts.appendChild(parent);   function appendDiv(parent, html, className) {       var div = document.createElement("div");       div.innerHTML = html;       div.className = className;       parent.appendChild(div);   回想一下,调用 xhr 的情况是提供 XML 然而并没有为我们解析XML 为对象,不同于 xhr 现在 SyndicationClient却包办搞定了,省却了循环的代码。结果虽然是一样,但其实后者会好一点。接下来介绍的数据绑定会干得更漂亮的说。 Unlike in the case of the xhr call, which provided XML but didn'tparse the RSS into objects for us, SyndicationClient does just that,simplifying our iteration code. The result looks the same, but thecode is a little nicer. And we can do even better by using templatesand data binding. 使用模板和数据绑定 UsingTemplates and Binding 看看 processPosts 函数与 appendDiv 函数结合的地方,你会发现那些静态的内容都被变量替换掉了。这种模式就是我们常说的模板(Template)。WinJS 考虑到这一功能,其语法如下所示: If you look at the combination of the processPosts and appendDivfunction, you notice that we're mixing static content with variablesubstitution in code. This pattern of UI creation is known as atemplate. WinJS come with a similar feature and the syntax looks likethis:     <meta charset="utf-8" />       <title>RssReader</title>       <!-- WinJS references -->       <link rel="stylesheet" href="/winjs/css/ui-light.css" />       <script src="/winjs/js/base.js"></script>       <script src="/winjs/js/ui.js"></script>       <script src="/winjs/js/binding.js"></script>       <script src="/winjs/js/controls.js"></script>       <script src="/winjs/js/animations.js"></script>       <script src="/winjs/js/uicollections.js"></script>       <script src="/winjs/js/wwaapp.js"></script>       <!-- RssReader references -->       <link rel="stylesheet" href="/css/default.css" />       <script src="/js/default.js"></script>   </head>   <body>       <h1>The Old New Thing</h1>       <div id="downloadStatus"></div>       <div id="posts"></div>       <div id="template" data-win-control="WinJS.Binding.Template">           <div data-win-bind="innerText: title" class="postTitle"></div>           <div data-win-bind="innerText: date" class="postDate"></div>           <div data-win-bind="innerHTML: content" class="postContent"></div>       </div>   </body>   </html>   欲使用 WinJS 模板,我们必须引用一组 WinJS 的 CSS 和JavaScript 文件。WinJS 围绕 data-win-control 属性提供了若干样式和行为,等等。对于HTML 元素,HTML5 规范了一组 data-*的属性以便去特定某些程序自定义的数据(译注,HTML5 data-*可参考http://dev.w3.org/html5/spec/Overview.html#embedding-custom-non-visible-data-with-the-data-attributes)。在该例子中,我们声明一个 WinJS.Binding.Template 的 JavaScript 构造器(constructor),该构造器获取了 div 以便为 div 登记相关的事件。顺便说一下,这也是在 WinJS 中声明一个控件的基础步骤。要触发 data-win-control 属性及其事件的执行,必须通过呼叫 WinJS.UI.processAll()。应该在哪个地方调用这个方法呢?事件 onmainwindowactivated 那里便最适合不过了。 To use WinJS templates, we must reference the bulk of the WinJSCSS and JavaScript files. These files provide the styles and behaviorfor the data-win-control attribute, among other things. HTML5 definesthe set of data-* attributes that we use for anything app-specific wewant to associate with an HTML element. In this case, we declare aJavaScript constructor function name WinJS.Binding.Template. Thisconstructor gets the associated div so that it can attach itself andprovide the necessary behavior. This is the foundation fordeclarative controls in WinJS. To cause the data-win-controlattributes to be executed, you must call WinJS.UI.processAll, whichis an excellent addition to your onmainwindowactivated event handler: [javascript] view plaincopy WinJS.Application.onmainwindowactivated = function (e) {       if (e.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {           // 下载           downloadStatus.innerText = "downloading posts...";           // use the WinRT to download the RSS           var syn = new Windows.Web.Syndication.SyndicationClient();           var url = new Windows.Foundation.Uri("http://blogs.msdn.com/b/oldnewthing/rss.aspx");           syn.retrieveFeedAsync(url).then(processPosts, downloadError);           // 执行已声明控件           WinJS.UI.processAll();   调用 processAll 则就是执行 WinJS.Binding.Template 的构造器函数,构造器内部具体的过程便是查找拥有属性 data-win-bind 的那些 div,代码如下: The call to processAll causes the WinJS.Binding.Templateconstructor to be called, which in turn parses the contents of thediv looking for data-win-bind attributes in the following format: <div data-win-bind=" elemAttrib1: dataProp1; elemAttrib2:dataProp2;..."></div>   如何构成数据绑定?请注意分号前面的部分为元素属性值(attribute)与分号后面的部分为 JavaScript 属性名称(property)。不管何种对象,模板只要遇到与属性(property)相吻合的情形即会渲染出来。于是,两者形成了绑定的关系,数据转化为特定的内容。所以模板这里的地方,我们的  processPosts 函数改为: The attribute establishes a data binding relationship between theelement attribute name before the colon (innerText or innerHTML inour sample) and the JavaScript property name after the colon. Thisproperty name is applied to whatever object is handed to the templatewhen it is asked to render, using the data to expand variablecontent. With this template in place, our processPosts functionchanges to: [javascript] view plaincopy   // 循环条目     for (var i = 0, len = feed.items.length; i < len; i++) {       var item = feed.items[i];       var post = {         title: item.title.text,         date: item.publishedDate,         content: item.summary.text,       // 渲染每一项的模板实例       template.render(post).then(function (element) {         // 向 #posts div添加数据         posts.appendChild(element);   函数 WinJS.UI.getControl 的参数是承托控件的元素,这里就是模板的div,getControl() 返回控件对象,实质是 JavaScript 对象,带有控件的状态和行为的对象。这里的行为就是每一项进行渲染时所消化的模板实例方法,就像刚才提到的 createElement 代码那样。模板的渲染函数是一个异步哈书,所以也需要 promise 一个函数,这里是添加帖子数据的过程。运行我们的更新程序所产生的输出与前面的过程一致,但因为模板的缩进就会看起来更清晰。在处理样式的处理时候,在HTML中编辑好模板会比 createElement 里面写简单很多。 The call to WinJS.UI.getControl function takes the elementassociated with the control, our template div in this case, andreturns the JavaScript object associated with it that is acting as acontrol. This control brings with it state and behavior. The behaviorwe want is the render function which stamps out an instance of thetemplate for each item we pass in, just like our createElement codewas doing before. This happens asynchronously in the case of thetemplate's render function, so we use a promise to append the newlycreated element to our set of posts. Running our updated programproduces output that looks exactly the same as before, but ourintent, with templates, is getting clearer. It's easier to edit thetemplate in the HTML than it is to write more and more complicatedcreateElement code as our styling and functionality grow. 不得不说,现在程序的样子离我们想像中的视窗应用程序差很远,列表内容太多了。实际上,多数 RSS Reader 只是会列出列表然后让你选择。我们也应该这样做,使用自带的ListView 控件做就好。 But our big list of all the content isn't as friendly as we want aWindows app to be. In fact, most RSS reader apps show a list of itemsand then let you select one to read. We can do that, too, using thebuilt-in ListView control. 添加控件 Adding Controls 除了模板控件和固有的控件外,尚有 WinJS内建的若干控件可用,例如 ListView 便是一个不错的控件,能够把数据渲染成为 Grid(表格)或 List (列表)。本例中是 Grid 布局。Grid 布局可以有效地分组、组织相关的数据。Grid 里的每一项又是模板来的,这正是利用了模板的原理。不妨想像一下,首页出现时候我们不打算显示全部内容,只要求显示标题,将多个标题形成一列表,帖子的列表。通过点击便可以看到全文内容。为此,default.html 添加了 ListView 控件,形如: In addition to the Template control and the HTML intrinsiccontrols, WinJS has several other built-in controls. For example, theListView control is a useful control that knows how to arrange datainto a grid or a list and, in the case of the grid layout, canorganize data into groups of related-data. Each item in the grid isan expansion of a template, as we've already seen. For example,imagine that we want to create a list of posts, but instead ofproviding the full content on the first page, want to click on anentry to see the content. Adding a ListView to default.html lookslike this: [html] view plaincopy <body>       <h1>The Old New Thing</h1>       <div id="downloadStatus"></div>       <div id="template" data-win-control="WinJS.Binding.Template">           <div data-win-bind="innerText: title" class="postTitle"></div>           <div data-win-bind="innerText: date" class="postDate"></div>       </div>       <div id="posts" data-win-control="WinJS.UI.ListView"            data-win-options="{itemRenderer: template, layout: {type: WinJS.UI.ListLayout}}"></div>   </body>   通过声明data-win-control 即可创建 ListView 控件,具体参数可透过data-win-options 属性其“名称-值”的结构,传入到ListView构造器(这是可选的参数)。本例中,ListView的每一项依然使用模板来决定它显示的值(没有内容的修改版本)和布局的类型(默认为grid layout)。指定 ListView 数据的方式仍然是我们熟悉的: Here we create the ListView control declaratively using"data-win-control". Also, use the data-win-optionsattribute as a set of name-value pairs that we pass as an optionalparameter to the ListView constructor. In this case, we declare thetemplate want to use for each item (a trimmed down version withoutthe content) and the kind of list layout we want (grid layout is thedefault). Populating the ListView with the data isn't very differentfrom what we've done so far:     downloadStatus.innerText = "";       for (var i = 0, len = feed.items.length; i < len; i++) {           var item = feed.items[i];           var post = {               title: item.title.text,               date: item.publishedDate,               content: item.summary.text,           postItems.push(post);       // 填入ListView 控件的数据源       posts.winControl.dataSource = postItems;   这一步,我们没有创建模板实例,而是建立data列表然后交给 ListView,即可自己渲染模板。值得一提的是,ListView 是一个聪明的控件,它会自动渲染那些实际显示的控件,现在我们创建 ListView 并填充数据: This time, instead of creating an instance of our template foreach item as we process it, we build up a list of data to hand to theListView, which itself renders the template for us. But the ListViewis much smarter about it, rendering only the template for items thatare actually visible. Now that we created our ListView and populatedit with data, we get: RSS Reader 还是半成品,如果弄好了内容区,那么就完全搞定了。下一辑的《运用JavaScript创建 Metro 式程序》,将演示如何通过加入阅读模板来增强程序的外观,为你程序平添视窗应用程序的风格与特征。 We now have half of an RSS reader. We still need the other half –that part where you can actually read the content. The next topic inthis series, Enhancing the presentation of your app, demonstrates howto complete your first Metro style app using JavaScript by adding areading pane, and using a Microsoft Visual Studio template that givesyour app the personality of Windows. 本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/archive/2012/02/10/2345197.html,如需转载请自行联系原作者

归纳一下:C#线程同步的几种方法

我们在编程的时候,有时会使用多线程来解决问题,比如你的程序需要在后台处理一大堆数据,但还要使用户界面处于可操作状态;或者你的程序需要访问一些外部资源如数据库或网络文件等。这些情况你都可以创建一个子线程去处理,然而,多线程不可避免地会带来一个问题,就是线程同步的问题。如果这个问题处理不好,我们就会得到一些非预期的结果。   在网上也看过一些关于线程同步的文章,其实线程同步有好几种方法,下面我就简单的做一下归纳。   一、volatile关键字   volatile是最简单的一种同步方法,当然简单是要付出代价的。它只能在变量一级做同步,volatile的含义就是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我。(【转自www.bitsCN.com 】)因此,当多线程同时访问该变量时,都将直接操作主存,从本质上做到了变量共享。   能够被标识为volatile的必须是以下几种类型:(摘自MSDN) Any reference type. Any pointer type (in an unsafe context). The types sbyte, byte, short, ushort, int, uint, char, float, bool. An enum type with an enum base type of byte, sbyte, short, ushort, int, or uint.  Code public class A {private volatile int _i;public int I {get { return _i; }set { _i = value; }   但volatile并不能实现真正的同步,因为它的操作级别只停留在变量级别,而不是原子级别。如果是在单处理器系统中,是没有任何问题的,变量在主存中没有机会被其他人修改,因为只有一个处理器,这就叫作processor Self-Consistency。但在多处理器系统中,可能就会有问题。 每个处理器都有自己的data cach,而且被更新的数据也不一定会立即写回到主存。所以可能会造成不同步,但这种情况很难发生,因为cach的读写速度相当快,flush的频率也相当高,只有在压力测试的时候才有可能发生,而且几率非常非常小。   二、lock关键字   lock是一种比较好用的简单的线程同步方式,它是通过为给定对象获取互斥锁来实现同步的。它可以保证当一个线程在关键代码段的时候,另一个线程不会进来,它只能等待,等到那个线程对象被释放,也就是说线程出了临界区。用法:  Code public void Function()  {object lockThis = new object (); lock (lockThis) {// Access thread-sensitive resources. }   lock的参数必须是基于引用类型的对象,不要是基本类型像bool,int什么的,这样根本不能同步,原因是lock的参数要求是对象,如果传入int,势必要发生装箱操作,这样每次lock的都将是一个新的不同的对象。最好避免使用public类型或不受程序控制的对象实例,因为这样很可能导致死锁。特别是不要使用字符串作为lock的参数,因为字符串被CLR“暂留”,就是说整个应用程序中给定的字符串都只有一个实例,因此更容易造成死锁现象。建议使用不被“暂留”的私有或受保护成员作为参数。其实某些类已经提供了专门用于被锁的成员,比如Array类型提供SyncRoot,许多其它集合类型也都提供了SyncRoot。   所以,使用lock应该注意以下几点:    1、如果一个类的实例是public的,最好不要lock(this)。因为使用你的类的人也许不知道你用了lock,如果他new了一个实例,并且对这个实例上锁,就很容易造成死锁。   2、如果MyType是public的,不要lock(typeof(MyType))   3、永远也不要lock一个字符串   三、System.Threading.Interlocked   对于整数数据类型的简单操作,可以用 Interlocked 类的成员来实现线程同步,存在于System.Threading命名空间。Interlocked类有以下方法:Increment , Decrement , Exchange 和CompareExchange 。使用Increment 和Decrement 可以保证对一个整数的加减为一个原子操作。Exchange 方法自动交换指定变量的值。CompareExchange 方法组合了两个操作:比较两个值以及根据比较的结果将第三个值存储在其中一个变量中。比较和交换操作也是按原子操作执行的。如:  Code int i = 0 ; System.Threading.Interlocked.Increment( ref i); Console.WriteLine(i); System.Threading.Interlocked.Decrement( ref i); Console.WriteLine(i); System.Threading.Interlocked.Exchange( ref i, 100 ); Console.WriteLine(i); System.Threading.Interlocked.CompareExchange( ref i, 10 , 100 ); Output:   四、Monitor   Monitor类提供了与lock类似的功能,不过与lock不同的是,它能更好的控制同步块,当调用了Monitor的Enter(Object o)方法时,会获取o的独占权,直到调用Exit(Object o)方法时,才会释放对o的独占权,可以多次调用Enter(Object o)方法,只需要调用同样次数的Exit(Object o)方法即可,Monitor类同时提供了TryEnter(Object o,[int])的一个重载方法,该方法尝试获取o对象的独占权,当获取独占权失败时,将返回false。   但使用 lock 通常比直接使用 Monitor 更可取,一方面是因为 lock 更简洁,另一方面是因为 lock 确保了即使受保护的代码引发异常,也可以释放基础监视器。这是通过 finally 中调用Exit来实现的。事实上,lock 就是用 Monitor 类来实现的。下面两段代码是等效的:  Code lock (x) DoSomething(); 等效于object obj = ( object )x; System.Threading.Monitor.Enter(obj);try { DoSomething(); }finally { System.Threading.Monitor.Exit(obj); 关于用法,请参考下面的代码:  Code private static object m_monitorObject = new object (); [STAThread]static void Main( string [] args) Thread thread = new Thread( new ThreadStart(Do)); thread.Name = " Thread1 " ; Thread thread2 = new Thread( new ThreadStart(Do)); thread2.Name = " Thread2 " ; thread.Start(); thread2.Start(); thread.Join(); thread2.Join(); Console.Read(); }static void Do() {if ( ! Monitor.TryEnter(m_monitorObject)) Console.WriteLine( " Can't visit Object " + Thread.CurrentThread.Name);return ; }try { Monitor.Enter(m_monitorObject); Console.WriteLine( " Enter Monitor " + Thread.CurrentThread.Name); Thread.Sleep( 5000 ); }finally { Monitor.Exit(m_monitorObject);   当线程1获取了m_monitorObject对象独占权时,线程2尝试调用TryEnter(m_monitorObject),此时会由于无法获取独占权而返回false,输出信息如下:   另外,Monitor还提供了三个静态方法Monitor.Pulse(Object o),Monitor.PulseAll(Object o)和Monitor.Wait(Object o ) ,用来实现一种唤醒机制的同步。关于这三个方法的用法,可以参考MSDN,这里就不详述了。   五、Mutex   在使用上,Mutex与上述的Monitor比较接近,不过Mutex不具备Wait,Pulse,PulseAll的功能,因此,我们不能使用Mutex实现类似的唤醒的功能。不过Mutex有一个比较大的特点,Mutex是跨进程的,因此我们可以在同一台机器甚至远程的机器上的多个进程上使用同一个互斥体。尽管Mutex也可以实现进程内的线程同步,而且功能也更强大,但这种情况下,还是推荐使用Monitor,因为Mutex类是win32封装的,所以它所需要的互操作转换更耗资源。   六、ReaderWriterLock   在考虑资源访问的时候,惯性上我们会对资源实施lock机制,但是在某些情况下,我们仅仅需要读取资源的数据,而不是修改资源的数据,在这种情况下获取资源的独占权无疑会影响运行效率,因此.Net提供了一种机制,使用ReaderWriterLock进行资源访问时,如果在某一时刻资源并没有获取写的独占权,那么可以获得多个读的访问权,单个写入的独占权,如果某一时刻已经获取了写入的独占权,那么其它读取的访问权必须进行等待,参考以下代码: Codeprivate static ReaderWriterLock m_readerWriterLock = new ReaderWriterLock();private static int m_int = 0; [STAThread]static void Main(string[] args) Thread readThread = new Thread(new ThreadStart(Read)); readThread.Name = "ReadThread1"; Thread readThread2 = new Thread(new ThreadStart(Read)); readThread2.Name = "ReadThread2"; Thread writeThread = new Thread(new ThreadStart(Writer)); writeThread.Name = "WriterThread"; readThread.Start(); readThread2.Start(); writeThread.Start(); readThread.Join(); readThread2.Join(); writeThread.Join(); Console.ReadLine();  }private static void Read() {while (true) Console.WriteLine("ThreadName " + Thread.CurrentThread.Name + " AcquireReaderLock"); m_readerWriterLock.AcquireReaderLock(10000); Console.WriteLine(String.Format("ThreadName : {0} m_int : {1}", Thread.CurrentThread.Name, m_int)); m_readerWriterLock.ReleaseReaderLock(); }private static void Writer() {while (true) Console.WriteLine("ThreadName " + Thread.CurrentThread.Name + " AcquireWriterLock"); m_readerWriterLock.AcquireWriterLock(1000); Interlocked.Increment(ref m_int); Thread.Sleep(5000); m_readerWriterLock.ReleaseWriterLock(); Console.WriteLine("ThreadName " + Thread.CurrentThread.Name + " ReleaseWriterLock"); 在程序中,我们启动两个线程获取m_int的读取访问权,使用一个线程获取m_int的写入独占权,执行代码后,输出如下: 可以看到,当WriterThread获取到写入独占权后,任何其它读取的线程都必须等待,直到WriterThread释放掉写入独占权后,才能获取到数据的访问权,应该注意的是,上述打印信息很明显显示出,可以多个线程同时获取数据的读取权,这从ReadThread1和ReadThread2的信息交互输出可以看出。   七、SynchronizationAttribute   当我们确定某个类的实例在同一时刻只能被一个线程访问时,我们可以直接将类标识成Synchronization的,这样,CLR会自动对这个类实施同步机制,实际上,这里面涉及到同步域的概念,当类按如下设计时,我们可以确保类的实例无法被多个线程同时访问 1). 在类的声明中,添加System.Runtime.Remoting.Contexts.SynchronizationAttribute属性。     2). 继承至System.ContextBoundObject     需要注意的是,要实现上述机制,类必须继承至System.ContextBoundObject,换句话说,类必须是上下文绑定的。     一个示范类代码如下: Code[System.Runtime.Remoting.Contexts.Synchronization]public class SynchronizedClass : System.ContextBoundObject   八、MethodImplAttribute   如果临界区是跨越整个方法的,也就是说,整个方法内部的代码都需要上锁的话,使用MethodImplAttribute属性会更简单一些。这样就不用在方法内部加锁了,只需要在方法上面加上 [MethodImpl(MethodImplOptions.Synchronized)] 就可以了,MehthodImpl和MethodImplOptions都在命名空间System.Runtime.CompilerServices 里面。但要注意这个属性会使整个方法加锁,直到方法返回,才释放锁。因此,使用上不太灵活。如果要提前释放锁,则应该使用Monitor或lock。我们来看一个例子:  Code [MethodImpl(MethodImplOptions.Synchronized)]public void DoSomeWorkSync() Console.WriteLine( " DoSomeWorkSync() -- Lock held by Thread " + Thread.CurrentThread.GetHashCode()); Thread.Sleep( 1000 ); Console.WriteLine( " DoSomeWorkSync() -- Lock released by Thread " + Thread.CurrentThread.GetHashCode()); }public void DoSomeWorkNoSync() Console.WriteLine( " DoSomeWorkNoSync() -- Entered Thread is " + Thread.CurrentThread.GetHashCode()); Thread.Sleep( 1000 ); Console.WriteLine( " DoSomeWorkNoSync() -- Leaving Thread is " + Thread.CurrentThread.GetHashCode()); [STAThread]static void Main( string [] args) MethodImplAttr testObj = new MethodImplAttr(); Thread t1 = new Thread( new ThreadStart(testObj.DoSomeWorkNoSync)); Thread t2 = new Thread( new ThreadStart(testObj.DoSomeWorkNoSync)); t1.Start(); t2.Start(); Thread t3 = new Thread( new ThreadStart(testObj.DoSomeWorkSync)); Thread t4 = new Thread( new ThreadStart(testObj.DoSomeWorkSync)); t3.Start(); t4.Start(); Console.ReadLine();  这里,我们有两个方法,我们可以对比一下,一个是加了属性MethodImpl的DoSomeWorkSync(),一个是没加的DoSomeWorkNoSync()。在方法中Sleep(1000)是为了在第一个线程还在方法中时,第二个线程能够有足够的时间进来。对每个方法分别起了两个线程,我们先来看一下结果: 可以看出,对于线程1和2,也就是调用没有加属性的方法的线程,当线程2进入方法后,还没有离开,线程1有进来了,这就是说,方法没有同步。我们再来看看线程3和4,当线程3进来后,方法被锁,直到线程3释放了锁以后,线程4才进来。   九、同步事件和等待句柄   用lock和Monitor可以很好地起到线程同步的作用,但它们无法实现线程之间传递事件。如果要实现线程同步的同时,线程之间还要有交互,就要用到同步事件。同步事件是有两个状态(终止和非终止)的对象,它可以用来激活和挂起线程。   同步事件有两种:AutoResetEvent和 ManualResetEvent。它们之间唯一不同的地方就是在激活线程之后,状态是否自动由终止变为非终止。AutoResetEvent自动变为非终止,就是说一个AutoResetEvent只能激活一个线程。而ManualResetEvent要等到它的Reset方法被调用,状态才变为非终止,在这之前,ManualResetEvent可以激活任意多个线程。   可以调用WaitOne、WaitAny或WaitAll来使线程等待事件。它们之间的区别可以查看MSDN。当调用事件的 Set方法时,事件将变为终止状态,等待的线程被唤醒。   来看一个例子,这个例子是MSDN上的。因为事件只用于一个线程的激活,所以使用 AutoResetEvent 或 ManualResetEvent 类都可以。 Codestatic AutoResetEvent autoEvent;static void DoWork() Console.WriteLine(" worker thread started, now waiting on event"); autoEvent.WaitOne(); Console.WriteLine(" worker thread reactivated, now exiting"); [STAThread]static void Main(string[] args) autoEvent = new AutoResetEvent(false); Console.WriteLine("main thread starting worker thread"); Thread t = new Thread(new ThreadStart(DoWork)); t.Start(); Console.WriteLine("main thrad sleeping for 1 second"); Thread.Sleep(1000); Console.WriteLine("main thread signaling worker thread"); autoEvent.Set(); Console.ReadLine();  我们先来看一下输出: 在主函数中,首先创建一个AutoResetEvent的实例,参数false表示初始状态为非终止,如果是true的话,初始状态则为终止。然后创建并启动一个子线程,在子线程中,通过调用AutoResetEvent的WaitOne方法,使子线程等待指定事件的发生。然后主线程等待一秒后,调用AutoResetEvent的Set方法,使状态由非终止变为终止,重新激活子线程。 1/MSDN(http://msdn.microsoft.com/zh-cn/library/ms173179(VS.80).aspx ) 2/http://www.cnblogs.com/VincentWP/archive/2008/06/25/1229104.html 本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/archive/2012/02/17/2356400.html,如需转载请自行联系原作者

public static bool ExchangeOrder<T>(this IList<T> list, int sourceID, int newID)         if (sourceID >= list.Count || sourceID < 0 || newID >= list.Count || newID < 0 || sourceID == newID) return false;             var temp = list[sourceID];             list[sourceID] = list[newID];             list[newID] = temp;             return true;         catch (Exception)             return false;     /// <summary>     /// 向上移动     /// </summary>     /// <typeparam name="T"></typeparam>     /// <param name="list">The 数据列表 list</param>     /// <param name="id">The 需要移动的id</param>     /// <param name="num">The 移动的 位数. 默认为1, 几位就向上移动几位</param>     /// <returns></returns>     public static bool MoveUpper<T>(this IList<T> list, int id, int num = 1)         return list.MoveItem(id, id - num);     //向下移动     public static bool MoveDown<T>(this IList<T> list, int id, int num = 1)         return list.MoveItem(id, id + num);     //移动到顶部     public static bool MoveTopper<T>(this IList<T> list, int id)         return list.MoveItem(id, 0);     //移动到底部     public static bool MoveBottom<T>(this IList<T> list, int id)         return list.MoveItem(id, list.Count -1);     /// <summary>     /// 移动     /// </summary>     /// <typeparam name="T"></typeparam>     /// <param name="list">数据列表</param>     /// <param name="sourceID">原来的数据ID</param>     /// <param name="newID">移动后数据ID</param>     /// <returns></returns>     public static bool MoveItem<T>(this IList<T> list, int sourceID, int newID)         if (sourceID >= list.Count || sourceID < 0 || newID >= list.Count || newID < 0 || sourceID == newID) return false;             var temp = list[sourceID];             list.RemoveAt(sourceID);             list.Insert(newID, temp);             return true;         catch (Exception)             return false;

闲话少说,书归正传: 1 Metro C++程序的入口点:     C++开发的Metro程序有两种架构:Windows RT和Direct程序,这两种程序可以完美的进行交互,用一个不恰当的例子形容他们之间的关系应该就是 MFC和Win32 API程序之间的关系。Windows RT是将Windows API封装在了一组类库中,从用户角度来说它处于更高层次,当然受到的局限性也更大,它的入口点被封装在IDE(vs2011)自动生成的app类中,该类被manifest指定为入口点。而Direct程序更像是用Win32 API开发的窗口程序,它的入口点是我们熟悉的main()。我们首先要实现两个类分别继承于IFramworkView和IFramwordViewSource,然后在main函数中创建IFrameworkViewSource接口实例,用该实例作为参数调用静态方法CoreApplication::Run(),并在IFrameworkViewSource接口的CreateView方法中创建IframeworkView实例,并调用IFrameworkView接口实例相关的方法(由自己实现)启动程序。 2 Metro UI:    紧接上文,使用 Windows RT开发的应用程序其实就是.Net程序的变种,通过 XAML定义Ui布局,使用丰富的控件可以进行比较简便快捷的开发出绚丽夺目的软件。但是它同样具备一些缺点:对效率的要求比较低,不够灵活只能使用控件提供的功能,无法访问一些底层的API,对于有特定需求的开发者(系统级软件)无法访问底层的API。另外,对于习惯了直接使用Win32开发窗口程序的程序员来说不容易适应。相反,Direct程序就是对Win32窗口程序的一种延伸,你可以自由创建或者获得CoreWindow这种对象(类似于窗口句柄),并在CoreWindow上直接使用Direct函数绘制(取代Gdi和GDI+)。同时你还可以使用dispatcher/event传递消息(取代message机制)。使用WRT直接使用Com组建,以及一些列Windows为Metro保留或者重新开发的Win32 API。然而,Direct程序开发复杂,难度大,繁琐的API调用必定会延长开发周期,因此究竟如何选择还需要根据实际情况而定。 对于我来说,我选择两者混用,在交互方面微软做的还是非常好的,基本是无缝对接。 3 进程与UI(题外话):     Windows程序通常分为两部分:进程和UI。当然,这么说非常不准确,事实上我想表达的意思其实是进程未必一定是要有UI的,即便没有UI程序依然也可以执行。因此,如果我们想真正弄懂Metro的机制,必须区分开进程与UI。特别值得一提的是,我们必须清楚地知道永远是进程驾驭UI,而非UI左右进程。这句话的意思并非是在UI中不能创建进程,而是说我们必须把进程作为程序的核心,我们需要UI的时候就去 创建,当UI不存在的时候,不意味进程就结束了,依然可以做我们想做的事,在需要的时候还可以再创建UI。当然,如果你只想开发一个WinRT程序,也未必要理解这些,就好像很多未曾深入研究的MFC程序员,它们觉得窗口就是和程序共存的(同生同灭),这个不难理解,因为窗口不是自己创建(微软帮你做了大部分工作)的,你要做的就是在UI中添代码,或许对你来有一种错觉就是现有UI后有进程。。。有了上面的一些不成熟的见解,我要引出的是几个类CoreApplication,CoreApplicationView,CoreWindow。这几个类表示的是什么?其实CoreApplication表示的就是进程,CoreWindow表示的是UI(类似于窗口句柄),CoreApplicaitonView就是将CoreWindow和CoreApplication联系起来的对象。对应到Windows窗口程序,CoreApplication就是表示执行main函数的一个进程,CoreWindow就是在这个进程中创建的一个窗口。由于Metro程序本质上就是一个拥有一个主窗口的全屏程序,因此需要使用CoreApplicationView将这个进程和一个主窗口绑定。通过CoreApplication::GetCurrentView获得当前进程的绑定关系CoreApplicationView,再通过CoreApplicationView::CoreWindow属性就可以找到绑定到当前进程的主窗口对象CoreWindow。 4 WinRT UI 结构:     承接上文,Metro程序的Ui其实都是基于一个全屏主窗口创建的。WinRT和MFC,WPF或者C#一样强化了UI而淡化了非UI部分,使得写程序看似就是一个在UI框架中填代码的操作,其实这么看也无可厚非,还能简化程序开发的难度。我们就看一看,到底使用 XAML创建的Ui是一个什么结构?这里首先要提到一个类Windows::UI::Xaml::Window。这个东西究竟是什么?从MSDN上看"A Window object is just surfacing information from CoreWindow, which in turn is referencing the window created by the system."这个我研究了很久,一直没明白,有几点是我感觉到值得特别注意的: 1 )这个类有获取CoreWindow的方法,但是却找不到任何从CoreWindow获取Window的方法。 2 )它属于Xaml空间 3 )它与UIElement有关系,我们之前说了MetroUI都是基于一个CoreWindow上的,但是从CoreWIndow我并没有找到与UI有什么关系。 通过以上几点我可以大胆推测:Window就是主窗口的CoreWindow的一个XAML表现形式,它将作为所有UIElment的父窗口。这样一来,CoreWindow就和UIElement挂钩了。之所以说它只表示主窗口的CoreWindow,是因为我们无法从任意的CoreWindow获得Window,而只能从一个Window类的静态属性Current得到唯一的Window对象,这只可能是主窗口的Window。 通过不断地实验,我大致可以得出Metro的Ui结构,一个CoreApplication绑定一个CoreWindow,这个CoreWIndow作为所有UIElement的父控件的表现形式就是Window。Window的Content属性是Frame,一个Window对应n个Frame也就是软件可以有很多场景,每个frame绑定n个page,这个page也就是你当前页面下所有控件的父节点。你可以在一个场景中自由切换页面,也可以在Window下自由切换场景,这对游戏开发者有着非凡的意义。对于一般非游戏开发者,可能一个frame多个page的情况比较常见。 5 page切换: 使用frame的Navigate()函数或者GoBack(),GoForward()。后两个函数没什么可说的,像浏览器一样,可以根据你的切换历史,跳转到指定页面。值得一题的是Navigate函数,很奇怪,它的参数并不是指定页面对象的名字,反而是指定页面的类型名。我大胆推测一下吧,在Metro程序中我们没有显式的创建过页面,而系统自动创建了我们自定义的页面,和可能是我们每一个自定义的页面类型只会被创建唯一一个实例,而且我们并不是使用ref new来创建,而是通过navigate函数创建这唯一的实例,因此我们只要指定页面的类型就可以跳转到唯一的页面。不过这只是我的推测。 本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/archive/2012/07/26/2609840.html,如需转载请自行联系原作者

[转]Android高手进阶教程(五)之----Android 中LayoutInflater的使用!

声明:eoe文章著作权属于作者,受法律保护,转载时请务必以超链接形式附带如下信息 原文作者: Android_Tutor 原文地址: http://my.eoe.cn/androidtutor/archive/1327.html 大家好我们这一节讲的是LayoutInflater的使用,在实际开发种LayoutInflater这个类还是非常有用的,它的作用类似于 findViewById() 不同点是LayoutInflater是用来找layout下xml布局文件,并且实例化!而findViewById()是找具体xml下的具体 widget控件(如:Button,TextView等)。为了让大家容易理解我做了一个简单的Demo,主布局main.xml里有一个TextView和一个Button,当点击Button,出现 Dialog,而这个Dialog的布局方式是我们在layout目录下定义的custom_dialog.xml文件(里面左右分布,左边 ImageView,右边TextView)。 效果图如下: 下面我将详细的说明Demo的实现过程: 1、新建一个 Android工程,我们命名为LayoutInflaterDemo. 2、修改main.xml布局,里面主要在 原来基础上增加了一个Button.代码如下: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="ShowCustomDialog" /> </LinearLayout> 3.定义对话框的布局方式,我们在layout目录下,新建一个名为 custom_dialog.xml文件具体代码如下: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" android:padding="10dp" <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_marginRight="10dp" /> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="fill_parent" android:textColor="#FFF" /> </LinearLayout> 4.修改主程序LayouInflaterDemo.java代码如下: package com.android.tutor; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; public class LayoutInflaterDemo extends Activity implements OnClickListener { private Button button; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); button = (Button)findViewById(R.id.button); button.setOnClickListener(this); @Override public void onClick(View v) { showCustomDialog(); public void showCustomDialog() AlertDialog.Builder builder; AlertDialog alertDialog; Context mContext = LayoutInflaterDemo.this; //下面俩种方法都可以 ////LayoutInflater inflater = getLayoutInflater(); LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE); View layout = inflater.inflate(R.layout.custom_dialog,null); TextView text = (TextView) layout.findViewById(R.id.text); text.setText("Hello, Welcome to Mr Wei's blog!"); ImageView image = (ImageView) layout.findViewById(R.id.image); image.setImageResource(R.drawable.icon); builder = new AlertDialog.Builder(mContext); builder.setView(layout); alertDialog = builder.create(); alertDialog.show(); 5、最后执行之,点击Button,将得到上述效果。好 今天就到此为止,睡觉了,大家有什么不明白的请留言~谢谢! 本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/archive/2013/03/13/2958168.html,如需转载请自行联系原作者

没有权限的话最好是通过命令来解决,linux下有超级用户(root)和普通用户,普通用户不能直接操作没有权限的目录。 分两种解决办法介绍: 打开终端:alt+f2,输入gnome-terminal,回车,在弹出的界面操作: 1.输入sudo nautilus回车,输入你的用户的密码,这样就打开了一个超级用户权限的资源管理器,然后你直接创建你的目录就行了。 2.用命令创建你的目录,先cd到你要创建目录的地址,比如cd /opt,然后sudo mkdir 目录名,按提示输入用户密码即可。 本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/archive/2013/05/15/3080248.html,如需转载请自行联系原作者

部署时服务端Excel的COM设置

在利用excel开发报表时,经常是在本机开发好了,但是一部署到服务器上就会出现COM的错误,大部分时候都是由于权限问题引起的。 在本地开发调试的时候,调用COM时一般都是本机管理员帐户(一般人登录本机都是管理员帐户)。而部署在服务器上时,运行程序的往往都不是管理员帐户,比如我们的报表功能如果集成在ASP.NET站点里,那么调用COM的就是IIS的帐户。此时就有可能出现权限问题。 因此我们需要加大IIS帐户的调用COM的权限。(以Windows2003 R2 64bit系统为例) 1.在[开始]—>[运行]—>输入[DCOMCNFG]打开组件的配置 2.配置NETWORK SERVICE的权限,如下图,用的是日文系统(呵呵!) 3.在64bit的系统上,如上设置以后,有时候还会出现调用COM后无响应的情况。再进行如下设置: 如上设置如果还有问题,可以参考微软的官方说明如下: 服务器端 Office 自动化注意事项 如何将 Office 应用程序配置为从 COM+/MTS 程序包自动运行 本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/archive/2010/05/10/1731574.html,如需转载请自行联系原作者

《CLR Via C# 第3版》笔记之(一) - CLR版本及编译平台

久闻这本书的大名,终于有中文版的了(英文太差没办法)。希望通过学习本书能够对CLR和.net有更深刻的了解,并且通过blog记录一些平时不太留意的地方和心得体。 主要内容: 通过CLRVer.exe查看本机的CLR版本 编译平台对最终生成的程序的影响 1. 通过CLRVer.exe查看本机的CLR版本 在.net SDK中有查看CLR版本的工具CLRVer.exe。 具体位置类似:C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools (我是64bit的系统。) 直接使用命令CLRVer.exe就能查看目前安装的CLR版本,我使用的是powershell,命令及结果如下: PS C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools> .\clrver.exe Versions installed on the machine: v2.0.50727 v4.0.30319 CLRVer.exe加上 –all 参数或进程ID还可以查看当前正在运行的进程所使用的CLR版本 PS C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools> .\clrver.exe -all3152    powershell_ise.exe      v2.0.50727 684    WindowsLiveWriter.exe    v2.0.50727 从中可以看出powershell_ise和LiveWriter都是基于CLRv2.0.50727的。 注:这里也可以看出CLR的版本和.net framework的版本不是一回事。 因为powershell_ise用来WPF的技术,应该是.net framework3.0或者3.5的。但是CLR版本仍是2.0的。 2. 编译平台对最终生成的程序的影响 C# 在编译时可以指定/platform选项选择编译的平台。目前有四个选项,ANYCPU x86 x64 Itanium。 /platform对程序的影响如下表: /platform 生成的托管模块 x86 Windows x64 windows IA64 Windows anycpu 不明确指定 作为32bit应用程序运行 作为64bit应用程序运行 作为64bit应用程序运行 作为32bit应用程序运行 作为WoW64应用程序运行 作为WoW64应用程序运行 作为64bit应用程序运行 Itanium Itanium 作为64bit应用程序运行 注:关于WoW64技术 WoW64(Windows on Windows64):有Windows 64位版本提供的一个技术,允许运行32位Windows程序,WoW64能够模拟x86指令,但是这样做会显著的影响性能。 本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/archive/2011/04/10/CLR_via_csharp_1.html,如需转载请自行联系原作者

分别编译为assembly和module。 d:\Vim\csharp>csc /t:library /out:MyAssembly.dll MyAssembly.cs Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1 d:\Vim\csharp>csc /t:module /out:MyModule.netmodule MyModule.cs Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1 生成的程序集和模块分别为:MyAssembly.dll,MyModule.netmodule。生成后使用ILDASM.exe分别来查看其元数据。查看方法:ILDASM | View | MetaInfo | Show! 比较MyAssembly.dll和MyModule.netmodule的元数据,我们发现MyAssembly.dll明显多了以下信息。 Assembly ------------------------------------------------------- Token: 0x20000001 Name : MyAssembly Public Key : Hash Algorithm : 0x00008004 Version: 0.0.0.0 Major Version: 0x00000000 Minor Version: 0x00000000 Build Number: 0x00000000 Revision Number: 0x00000000 Locale: <null> Flags : [none] (00000000) CustomAttribute #1 (0c000001) ------------------------------------------------------- CustomAttribute Type: 0a000001 CustomAttributeName: System.Runtime.CompilerServices.CompilationRelaxationsAttribute :: instance void .ctor(int32) Length: 8 Value : 01 00 08 00 00 00 00 00 > < ctor args: (8) CustomAttribute #2 (0c000002) ------------------------------------------------------- CustomAttribute Type: 0a000002 CustomAttributeName: System.Runtime.CompilerServices.RuntimeCompatibilityAttribute :: instance void .ctor() Length: 30 Value : 01 00 01 00 54 02 16 57 72 61 70 4e 6f 6e 45 78 > T WrapNonEx< : 63 65 70 74 69 6f 6e 54 68 72 6f 77 73 01 >ceptionThrows < ctor args: () 即表明MyAssembly.dll为一个程序集。正因为有了这些信息,MyAssembly.dll才可以发布和部署。 2. 多文件程序集 一般我们自己编译的程序集都是单个文件的(一个exe或者一个dll)。那么,如何编译出多文件的程序集呢? 多文件的程序集有以下的优势: 发布后,程序集有多个文件,只须下载所需的文件就能运行程序集,不用全部下载。 发布后,如果程序集有更新,只须下载更新的部分即可,不用重新下载整个程序集。 下面进行简单的实验 首先,建立3个文件,Module1.cs, Module2.cs, Program.cs。内容如下: // file:Module1.cs using System; namespace CLRViaCSharp public class Module1 public void Module1_method() Console.WriteLine("this is the original module1"); // file:Module2.cs using System; namespace CLRViaCSharp public class Module2 public void Module2_method() Console.WriteLine("this is the original module2"); // file :Program.cs using System; namespace CLRViaCSharp class Program static void Main(string[] args) Module1 m1 = new Module1(); Module2 m2 = new Module2(); m1.Module1_method(); m2.Module2_method(); 然后,分别将Module1.cs和Module2.cs编译为模块,将此两个模块和Program.cs编译为一个程序集。 D:\Vim\csharp>csc /t:module Module1.cs Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1 D:\Vim\csharp>csc /t:module Module2.cs Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1 D:\Vim\csharp>al /t:library /out:Module.dll Module1.netmodule Module2.netmodule Microsoft (R) Assembly Linker version 10.0.30319.1 D:\Vim\csharp>csc Program.cs /r:Module.dll Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1 注意其中第三步是用来al.exe这个程序集链接器来链接dll的。 执行编译好的Program.exe,结果如下: D:\Vim\csharp>Program.exe this is the original module1 this is the original module2 然后我们修改一下Module1.cs文件,修改Console.WriteLine的内容。 using System; namespace CLRViaCSharp public class Module1 public void Module1_method() Console.WriteLine("this is the update module1"); 只重新编译一下Module1.cs,注意此处不重新编译Module.dll和Program.exe 再次执行Program.exe时,结果就改变了。 D:\Vim\csharp>csc /t:module Module1.cs Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1 D:\Vim\csharp>Program.exe this is the update module1 this is the original module2 这个例子中的Module.dll就是个多文件的程序集,它并没有将Module1.netmodule和Module2.netmodule的内容加到Module.dll中,只是在Module.dll中保存了Module1.netmodule和Module2.netmodule的引用。 查看Module.dll的元数据也可以看出这点,Module.dll中只有清单(Manifest),没有实际的IL内容。 本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/archive/2011/04/24/2026530.html,如需转载请自行联系原作者

1. const和readonly的区别 主要的区别在于 const是在编译时确定值的,readonly是在运行时确定值的。 因此,用const修饰的字段,必须在定义的时候就赋值,否则编译器报错。 而readonly修饰的字段除了可以在定义时赋值以外,还可以在构造函数中赋值。 验证的代码如下:         IL_0029: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey(bool)         IL_002e: pop         IL_002f: ret     } // End of method CLRviaCSharp_7.Main } // End of class Test7.CLRviaCSharp_7 从Main函数中的IL_0007,我们可以看出,编译时就已经将 cValue替换为字符串"const"了,所以在Main函数中看不出使用了字段cValue 从Main函数中的IL_0018,我们可以看出,运行时才读取 rValue的值,将其拼接到输出的字符串中。 2. readonly的补充说明 const和readonly虽然都可以定义常量,但是由于readonly是在运行时才确定值的,所以比const更加灵活。 当然,readonly的性能肯定比const稍逊。(具体相差多少还没有试验过) readonly与const相比,使用时需要注意2点。 2.1. readonly的字段可以通过反射来修改 具体参见以下代码

《CLR Via C# 第3版》笔记之(十一) - 匿名类型和元组

匿名类型使得C#更具动态性。而元组则是以前看python时就了解的一个类型,与List类型类似,只是它里面的元素是不可变的。 主要内容: 匿名类型介绍 元组的介绍 1. 匿名类型介绍 匿名类型其实也像元组一样,定义了以后就不能更改里面元素的名称和个数。 使用匿名类型的代码非常简单: using System; namespace cnblog_bowen public sealed class CLRviaCSharp_11 static void Main(string[] args) var v = new { Name="my name", Age=100 }; Console.WriteLine("Name={0}, Age={1}", v.Name, v.Age); Console.ReadKey(true); 在第一次定义的时候指定元素的名称,元素的类型是由系统自动推断的。定义完后就可以可以当成一个实例来使用了。 现在visual studio也可以“智能感知”匿名类型的属性名了。 匿名类型的一些规则: 1)如果两个匿名类型的结构完全一样,那么它们之间可以进行赋值操作 2)匿名类型是引用类型,编译器会生成相应的class,而不是struct 3)匿名类型也是Object类派生的,但是无法将Object类型转型回匿名类型。 4)匿名类型不能作为一个方法的参数和返回值。 上述3)和4)的原因在于,匿名类型的名称是有编译器按一定规则生成的,在写代码的时候并不知道我们定义的匿名类型的具体名称。 因此,无法从Object转型回匿名类型,也不能指定方法的参数类型和返回值类型。 2. 元组的介绍 C#中的泛型元组类型重载了多个版本,用于创建各种参数类型和个数的元组。 创建元组的方法如下所示: namespace System // Summary: // Provides static methods for creating tuple objects. public static class Tuple public static Tuple<T1> Create<T1>(T1 item1); public static Tuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2); public static Tuple<T1, T2, T3> Create<T1, T2, T3>(T1 item1, T2 item2, T3 item3); public static Tuple<T1, T2, T3, T4> Create<T1, T2, T3, T4>(T1 item1, T2 item2, T3 item3, T4 item4); public static Tuple<T1, T2, T3, T4, T5> Create<T1, T2, T3, T4, T5>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5); public static Tuple<T1, T2, T3, T4, T5, T6> Create<T1, T2, T3, T4, T5, T6>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6); public static Tuple<T1, T2, T3, T4, T5, T6, T7> Create<T1, T2, T3, T4, T5, T6, T7>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7); public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8>> Create<T1, T2, T3, T4, T5, T6, T7, T8>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8); 元组创建后就是只读的了,不能在对其中的元素进行修改。 元组的一些规则: 1)元组中的字段都是以Item#的方式命名的(即Item1,Item2……) 2)写Tuple和用Tuple的人必须对Item#属性放回的内容有个清楚的了解 3)Tuple类型可以作为方法的参数和返回值 本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/archive/2011/07/08/2100788.html,如需转载请自行联系原作者

C#直接读取磁盘文件(类似linux的Direct IO模式)

由于项目需要测试windows下的IO性能,因此要写个小程序,按照要求读取磁盘上的文件。在读取文件的时候,测试Windows的IO性能。 主要内容: 程序的要求 一般的FileStream方式 利用kernel32.dll中的CreateFile函数 1. 程序的要求 程序的要求很简单。 (1)命令行程序 (2)有3个参数,读取的文件名,一次读取buffer size,读取的次数count (3)如果读取次数count未到,文件已经读完,就再次从头读取文件。 使用格式如下: C:\>****.exe “c:\****.bin” 32768 32768 读取文件“c:\****.bin”,每次读取4K,读取32768次,读取的量大概1G。 2. 一般的FileStream方式 利用FileStream来读取文件,非常简单,代码如下: 编译后的exe文件可以按照既定要求执行,但是对于同一文件,第二次读取明显比第一次快很多(大家可以用个1G左右的大文件试试)。第三次读取,第四次读取……和第二次差不多,都很快。 基于上述情况,可以判断是缓存的原因,导致第二次及以后各次都比较快。 但是从代码中来看,已经执行了input.Flush();input.Close();甚至是GC.Collect(); 所以可能是Windows系统或者CLR对文件读取操作进行了优化,使用了缓存。 3. 利用kernel32.dll中的CreateFile函数 既然上述方法行不通,就得调查新的方法。通过google的查询,大部分人都是建议用C/C++调用系统API来实现。 不过最后终于找到了用c#实现了无缓存直接读取磁盘上的文件的方法。其实也是通过DllImport利用了kernel32.dll,不完全是托管代码。(估计用纯托管代码实现不了) 参考的文章:How do I read a disk directly with .Net? 还有msdn中的CreateFile API 实现代码就是参考的How do I read a disk directly with .Net?,分为两部分 (1)利用CreateFile API构造的可直接读取磁盘的DeviceStream         /// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and         /// (offset + count - 1) replaced by the bytes read from the current source. </param>         /// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream. </param>         /// <param name="count">The maximum number of bytes to be read from the current stream.</param>         /// <returns></returns>         public override int Read(byte[] buffer, int offset, int count)             int BytesRead = 0;             var BufBytes = new byte[count];             if (!ReadFile(handleValue.DangerousGetHandle(), BufBytes, count, ref BytesRead, IntPtr.Zero))                 Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());             for (int i = 0; i < BytesRead; i++)                 buffer[offset + i] = BufBytes[i];             return BytesRead;         public override int ReadByte()             int BytesRead = 0;             var lpBuffer = new byte[1];             if (!ReadFile(             handleValue.DangerousGetHandle(),                        // handle to file             lpBuffer,                // data buffer             1,        // number of bytes to read             ref BytesRead,    // number of bytes read             IntPtr.Zero             { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); ;}             return lpBuffer[0];         public override long Seek(long offset, SeekOrigin origin)             throw new NotImplementedException();         public override void SetLength(long value)             throw new NotImplementedException();         public override void Write(byte[] buffer, int offset, int count)             throw new NotImplementedException();         public override void Close()             handleValue.Close();             handleValue.Dispose();             handleValue = null;             base.Close();         private bool disposed = false;         new void Dispose()             Dispose(true);             base.Dispose();             GC.SuppressFinalize(this);         private new void Dispose(bool disposing)             // Check to see if Dispose has already been called.             if (!this.disposed)                 if (disposing)                     if (handleValue != null)                         _fs.Dispose();                         handleValue.Close();                         handleValue.Dispose();                         handleValue = null;                 // Note disposing has been done.                 disposed = true;                 Console.WriteLine("Total cost " + (new TimeSpan(DateTime.Now.Ticks - start)).TotalSeconds + " seconds");             catch (Exception ex)                 Console.WriteLine(ex.Message);             finally                 if (input != null)                     input.Close();                 //Console.ReadKey(true);

mono和monodevelop源码编译安装

之所以用源码编译的方式安装mono和monodevelop,是因为通过yum安装的mono不是最新版本,而且monodevelop不能建 asp.net MVC3的工程。 而且通过源码安装,可以进一步了解mono的各个项目之间的关系。 我用的Fedora16系统 1.  mono的源码编译安装 下载mono的最新源码,github上的源码编译时总是报找不到 gmcs.exe的错误。 我是在mono的官网上下的最新源码,地址:https://wrench.mono-project.com/Wrench/index.aspx?show_all=true 我下的是mono-2.11.3版 # tar jxvf mono-2.11.3.tar.bz2 # cd mono-2.11.3 # ./autogen.sh --prefix=/usr/local # make # make install 需要注意的是用root用户来安装,否则有错误。 2. monodevelop的源码编译安装 monodevelop的源码编译安装相当曲折,搞了好几天。 有几点需要注意: 在monodevelop的官网上下最新源码,否则在./configure时会有找不到文件的错。 在安装monodevelop之前,要先安装一些依赖的包。(mono-addins,gtk-sharp,libglade2,gnome-sharp-2.0) 安装完后,需要设置LD_LIBRARY_PATH这个环境变量 用root用户来安装 安装monodevelop之前,按顺序安装一些依赖包: (gtk-sharp  --> mono-addins --> libglade2--> gnome-sharp-2.0) 这些包在安装时也会遇到各种问题,下面是我安装时遇到的各种问题及解决方法,供参考。 以上的安装顺序是我总结出来的,没有试验过。 我安装时并不知道上面的安装顺序,是直接安装monodevelop,然后遇到问题再解决的。我的安装顺序如下: 1) 和mono同样的网址下载monodevelop的最新代码。 # tar jxvf monodevelop-3.1.0.tar.bz2 # cd monodevelop-3.1.0 # ./configure --prefix=`pkg-config --variable=prefix mono` 此时报了一个错误如下:找不到 UNMANAGED_DEPENDENCIES_MONO ==================================================================== [root@localhost monodevelop-3.1.0]# ./configure --prefix=`pkg-config --variable=prefix mono` checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for a thread-safe mkdir -p... /bin/mkdir -p checking for gawk... gawk checking whether make sets $(MAKE)... yes checking how to create a ustar tar archive... gnutar checking whether to enable maintainer-specific portions of Makefiles... no checking for mono... /usr/local/bin/mono checking for gmcs... /usr/local/bin/gmcs checking for update-mime-database... /usr/bin/update-mime-database checking for pkg-config... /usr/bin/pkg-config checking for msgfmt... /usr/bin/msgfmt checking for msgmerge... /usr/bin/msgmerge checking pkg-config is at least version 0.9.0... yes checking for UNMANAGED_DEPENDENCIES_MONO... no configure: error: Please install mono version 2.8 or later to install MonoDevelop. Please see http://www.mono-project.org/ to download latest mono sources or packages ==================================================================== 通过以下命令修复此错误: # export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig 参考网址: http://mono.1490590.n4.nabble.com/can-t-install-monodevelop-1-9-1-after-installed-mono-2-4-preview2-td1516079.html 再次执行: # ./configure --prefix=`pkg-config --variable=prefix mono` 此时报了一个错误如下:找不到 mono-addins ==================================================================== checking for UNMANAGED_DEPENDENCIES_MONO... yes checking for mono... /usr/local/bin/mono checking for dmcs... /usr/local/bin/dmcs checking for MONO_ADDINS... no configure: error: Package requirements (mono-addins >= 0.6) were not met: No package 'mono-addins' found Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables MONO_ADDINS_CFLAGS and MONO_ADDINS_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. ==================================================================== 下载mono-addins并安装, 下载地址: http://download.mono-project.com/sources/mono-addins/ # tar jxvf mono-addins-0.6.2.tar.bz2 # cd mono-addins-0.6.2 # ./configure --prefix=/usr/local 此时报了一个错误如下:找不到 gtk-sharp-2.0 ==================================================================== checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for a thread-safe mkdir -p... /bin/mkdir -p checking for gawk... gawk checking whether make sets $(MAKE)... yes checking how to create a ustar tar archive... gnutar checking whether to enable maintainer-specific portions of Makefiles... no checking for pkg-config... /usr/bin/pkg-config checking for a BSD-compatible install... /usr/bin/install -c checking for gmcs... /usr/local/bin/gmcs checking for gacutil... /usr/local/bin/gacutil checking for al... /usr/local/bin/al checking pkg-config is at least version 0.9.0... yes checking for GTK_SHARP_20... configure: error: Package requirements (gtk-sharp-2.0) were not met: No package 'gtk-sharp-2.0' found Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables GTK_SHARP_20_CFLAGS and GTK_SHARP_20_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. ==================================================================== 下载gtk-sharp并安装, 下载地址: http://download.mono-project.com/sources/gtk-sharp212/ # tar jxvf gtk-sharp-2.12.11.tar.bz2 # cd gtk-sharp-2.12.11 # ./configure --prefix=/usr/local # make # make install gtk-sharp安装成功后,接着安装mono-addins # cd ../mono-addins-0.6.2 # ./configure --prefix=/usr/local # make # make install mono-addins安装成功后,接着安装monodevelop    # cd ../monodevelop-3.1.0 # ./configure --prefix=`pkg-config --variable=prefix mono` 此时报了一个错误如下:找不到 glade-sharp-2.0 ==================================================================== [root@localhost monodevelop-3.1.0]# ./configure --prefix=`pkg-config --variable=prefix mono` checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for a thread-safe mkdir -p... /bin/mkdir -p checking for gawk... gawk checking whether make sets $(MAKE)... yes checking how to create a ustar tar archive... gnutar checking whether to enable maintainer-specific portions of Makefiles... no checking for mono... /usr/local/bin/mono checking for gmcs... /usr/local/bin/gmcs checking for update-mime-database... /usr/bin/update-mime-database checking for pkg-config... /usr/bin/pkg-config checking for msgfmt... /usr/bin/msgfmt checking for msgmerge... /usr/bin/msgmerge checking pkg-config is at least version 0.9.0... yes checking for UNMANAGED_DEPENDENCIES_MONO... yes checking for mono... /usr/local/bin/mono checking for dmcs... /usr/local/bin/dmcs checking for MONO_ADDINS... yes checking for MONO_ADDINS_SETUP... yes checking for MONO_ADDINS_GUI... yes checking for GLIB_SHARP... yes checking for GTK_SHARP... yes checking for GLADE_SHARP... no configure: error: Package requirements (glade-sharp-2.0 >= 2.12.8) were not met: No package 'glade-sharp-2.0' found Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables GLADE_SHARP_CFLAGS and GLADE_SHARP_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. ==================================================================== 我不知道glade-sharp是什么包,先安装了libgdiplus 下载libgdiplus并安装, 下载地址: http://download.mono-project.com/sources/libgdiplus/ # tar jxvf libgdiplus-2.10.9.tar.bz2 # cd libgdiplus-2.10.9 # ./configure --prefix=/usr/local # make make时报了一个错误如下: ==================================================================== /usr/lib64/libglib-2.0.so.0: could not read symbols: Invalid operation collect2: error: ld returned 1 exit status make[2]: *** [testgdi] Error 1 make[2]: Leaving directory `/usr/local/src/libgdiplus-2.10.9/tests' make[1]: *** [all-recursive] Error 1 make[1]: Leaving directory `/usr/local/src/libgdiplus-2.10.9' make: *** [all] Error 2 ==================================================================== 解决办法如下: (参考网址: http://www.sgvulcan.com/libgdiplus-2-10-9-build-fails-on-slacware-current64-2012-06/) 1. 运行./configure --prefix=/usr/local 之后,编辑 tests/Makefile 文件 2. 在Makefile文件130行位置,将   LIBS = -lpthread -lfontconfig                             改为 LIBS = -lpthread -lfontconfig -lglib-2.0 -lX11 3. 再次运行 make 即可 # make # make install 接着安装monodevelop   # cd ../monodevelop-3.1.0 # ./configure --prefix=`pkg-config --variable=prefix mono` 仍然是错误:找不到 glade-sharp-2.0 利用yum 安装libglade2-devel,mono源码中没找到这个包 参考网址: http://mono.1490590.n4.nabble.com/Monodevelop-2-4-compilation-error-quot-No-package-glade-sharp-2-0-found-quot-td2268343.html # yum search libglade2 # yum install libglade2-devel.x86_64 知道这个包的名称,也可以找相应的rpm来安装。 安装完libglade2后,重新安装 gtk-sharp2,安装方法上面已有。 接着,再次尝试安装 monodevelop: # ./configure --prefix=`pkg-config --variable=prefix mono` 此时报了一个错误如下:未安装gnome-sharp-2.0 ==================================================================== [root@localhost monodevelop-3.1.0]# ./configure --prefix=`pkg-config --variable=prefix mono` checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for a thread-safe mkdir -p... /bin/mkdir -p checking for gawk... gawk checking whether make sets $(MAKE)... yes checking how to create a ustar tar archive... gnutar checking whether to enable maintainer-specific portions of Makefiles... no checking for mono... /usr/local/bin/mono checking for gmcs... /usr/local/bin/gmcs checking for update-mime-database... /usr/bin/update-mime-database checking for pkg-config... /usr/bin/pkg-config checking for msgfmt... /usr/bin/msgfmt checking for msgmerge... /usr/bin/msgmerge checking pkg-config is at least version 0.9.0... yes checking for UNMANAGED_DEPENDENCIES_MONO... yes checking for mono... /usr/local/bin/mono checking for dmcs... /usr/local/bin/dmcs checking for MONO_ADDINS... yes checking for MONO_ADDINS_SETUP... yes checking for MONO_ADDINS_GUI... yes checking for GLIB_SHARP... yes checking for GTK_SHARP... yes checking for GLADE_SHARP... yes checking for MONODOC... yes checking for GNOME_SHARP... no checking for GNOME_VFS_SHARP... no checking for GCONF_SHARP... no configure: error: Cannot enable GNOME platform without gnome-sharp-2.0 ==================================================================== 下载 gnome-sharp-2.0 并安装 下载地址: http://download.mono-project.com/sources/gnome-sharp220/ # tar jxvf gnome-sharp-2.20.1.tar.bz2 # cd gnome-sharp-2.20.1 # ./configure --prefix=/usr/local 虽然没有报错,但是提示缺少3个assmely: art-sharp.dll,gnomevfs-sharp.dll,gnome-sharp.dll ==================================================================== Configuration summary * Installation prefix = /usr/local * C# compiler: /usr/local/bin/mcs -define:GTK_SHARP_2_6 -define:GTK_SHARP_2_8 -define:GNOME_SHARP_2_16 -define:GNOME_SHARP_2_20 Optional assemblies included in the build: * art-sharp.dll: no * gnomevfs-sharp.dll: no * gnome-sharp.dll: no NOTE: if any of the above say 'no' you may install the corresponding development packages for them, rerun autogen.sh to include them in the build. gnome-sharp.dll requires libgnomecanvas, libgnome, libgnomeui, libgnomeprint, libgnomeprintui, and libpanelapplet. ==================================================================== 下面的两个dll对应如下库:(我是通过 yum找到的) art-sharp.dll             : libart_lgpl-devel gnomevfs-sharp.dll: gnome-vfs2-devel 最后一个根据上面的提示,(对应如下库) gnome-sharp.dll   : libgnomecanvas, libgnome,             libgnomeui, libgnomeprint, libgnomeprintui, and             libpanelapplet(即gnome-panel-devel) # make make 后,报出如下错误: ==================================================================== error CS0006: Metadata file `Mono.GetOptions.dll' could not be found ==================================================================== 经过调查, 1.  原来是gnome-sharp版本太旧的原因,     前面用的gnome-sharp-2.20.*用了Mono.GetOptions,更新至gnome-sharp-2.24.*2.  注释掉gnome-sharp-2.24.1/sample/gnomevfs/Makefile中关于 Mono.GetOptions的内容 参考网址: http://www.puppeter.cn/?p=1126 下载地址:http://download.mono-project.com/sources/gnome-sharp2/ # tar jxvf gnome-sharp-2.24.1.tar.bz2 # cd gnome-sharp-2.24.0 # ./configure --prefix=/usr/local # make # make install 接着,再次尝试安装 monodevelop: # ./configure --prefix=`pkg-config --variable=prefix mono` 此时会产生一些找不到 tests/UnitTests/Makefile.in 之类的错误。 经过调查,似乎是monodevelop 版本的问题,在monodevelop官网上下载最新的代码, 然后重新安装,终于安装成功。官网上最新版是3.0.3.4 # tar jxvf monodevelop-3.0.3.4.tar.bz2 # cd ../monodevelop-3.0.3.4 # ./configure --prefix=`pkg-config --variable=prefix mono` # make # make install 安装完后,monodevep却无法启动,错误如下: ==================================================================== System.TypeInitializationException: An exception was thrown by the type initializer for Mono.Unix.Native.Syscall ---> System.DllNotFoundException: libMonoPosixHelper.so at (wrapper managed-to-native) Mono.Unix.Native.Syscall:_L_ctermid () at Mono.Unix.Native.Syscall..cctor () [0x00032] in /home/wangyb/downloads/mono/mono-2.11.3/mcs/class/Mono.Posix/Mono.Unix.Native/Syscall.cs:2599 --- End of inner exception stack trace --- at MonoDevelop.Core.LoggingService.RedirectOutputToFileUnix (FilePath logDirectory, System.String logName) [0x0001f] in /home/wangyb/downloads/mono/monodevelop-3.0.3.4/src/core/MonoDevelop.Core/MonoDevelop.Core/LoggingService.cs:177 at MonoDevelop.Core.LoggingService.RedirectOutputToLogFile () [0x00046] in /home/wangyb/downloads/mono/monodevelop-3.0.3.4/src/core/MonoDevelop.Core/MonoDevelop.Core/LoggingService.cs:140 FATAL ERROR [2012-07-29 07:59:49Z]: MonoDevelop failed to start. Some of the assemblies required to run MonoDevelop (for example gtk-sharp, gnome-sharp or gtkhtml-sharp) may not be properly installed in the GAC. System.TypeInitializationException: An exception was thrown by the type initializer for Gtk.Application ---> System.DllNotFoundException: glibsharpglue-2 at (wrapper managed-to-native) GLib.Thread:glibsharp_g_thread_supported () at GLib.Thread.get_Supported () [0x00000] in <filename unknown>:0 at Gtk.Application..cctor () [0x00000] in <filename unknown>:0 --- End of inner exception stack trace --- at MonoDevelop.Ide.IdeStartup.Run (MonoDevelop.Ide.MonoDevelopOptions options) [0x00085] in /home/wangyb/downloads/mono/monodevelop-3.0.3.4/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs:95 at MonoDevelop.Ide.IdeStartup.Main (System.String[] args) [0x00058] in /home/wangyb/downloads/mono/monodevelop-3.0.3.4/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs:503 ==================================================================== 将LD_LIBRARY_PATH加入到启动的配置文件中,即 ~/.bashrc 内容如下: # add mono LD_LIBRARY_PATH export LD_LIBRARY_PATH=/usr/local/lib 终于启动成功,也有 aspnet-mvc3的工程了 ^_^ 3. 总结 记录下安装过程的错误,也是方便以后遇到相同的错误可以知道怎么解决。 大家在安装过程中如果有什么新的问题,欢迎交流。 本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/archive/2012/07/29/2613844.html,如需转载请自行联系原作者

用ILSpy可以发现对应的IL代码如下: .class private auto ansi sealed delegate_test.Sum extends [mscorlib]System.MulticastDelegate // Methods .method public hidebysig specialname rtspecialname instance void .ctor ( object 'object', native int 'method' ) runtime managed } // end of method Sum::.ctor .method public hidebysig newslot virtual instance int32 Invoke ( int32 a, int32 b ) runtime managed } // end of method Sum::Invoke .method public hidebysig newslot virtual instance class [mscorlib]System.IAsyncResult BeginInvoke ( int32 a, int32 b, class [mscorlib]System.AsyncCallback callback, object 'object' ) runtime managed } // end of method Sum::BeginInvoke .method public hidebysig newslot virtual instance int32 EndInvoke ( class [mscorlib]System.IAsyncResult result ) runtime managed } // end of method Sum::EndInvoke } // end of class delegate_test.Sum 从中我们可以看出 委托是一个类,继承于System.MulticastDelegate。 委托类中有3个方法和一个构造函数 委托构造函数中需传入2个参数 为了更好的了解委托的类,下面写了个简单的例子先来了解一下委托的构造函数: using System; namespace delegate_test internal delegate int Sum(int a, int b); class Program public static void Main(string[] args) Console.WriteLine("Hello World!"); // 实例化委托,这里和委托的IL代码中构造函数不一致 // IL代码中 Sum 的构造函数需要传入2个参数(一个object 类型,一个 native int类型) .method public hidebysig specialname rtspecialname instance void .ctor ( object 'object', native int 'method' ) runtime managed } // end of method Sum::.ctor Sum s = new Sum(Add); Console.Write("Press any key to continue . . . "); Console.ReadKey(true); public static int Add(int a, int b) return a+b; 注意代码的注释,我们看出C#中实例化委托的写法与IL中看到的委托的构造函数不一致, 其实这只是C#的语法糖,为了方便代码编写,将上面的代码编译后,用ILSpy查看IL代码: IL_0000: nop IL_0001: ldstr "Hello World!" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop IL_000c: ldnull IL_000d: ldftn int32 delegate_test.Program::Add(int32, int32) IL_0013: newobj instance void delegate_test.Sum::.ctor(object, native int) 我们发现,实际运行时,还是构造了2个参数传给了委托的构造函数,一个null,还有一个是函数Add的地址。 这里的第一个参数object为null的原因是,Add函数是static,不用实例化一个object来调用它。 下面构造一个非static的函数Add2来看看object的值是否还为null。 using System; namespace delegate_test internal delegate int Sum(int a, int b); class Program public static void Main(string[] args) Console.WriteLine("Hello World!"); // 实例化委托,这里和委托的IL代码中构造函数不一致 // IL代码中 Sum 的构造函数需要传入2个参数(一个object 类型,一个 native int类型) .method public hidebysig specialname rtspecialname instance void .ctor ( object 'object', native int 'method' ) runtime managed } // end of method Sum::.ctor Sum s = new Sum(Add); // 非static函数 Add2 Sum s2 = new Sum((new Program()).Add2); Console.Write("Press any key to continue . . . "); Console.ReadKey(true); public static int Add(int a, int b) return a+b; public int Add2(int a, int b) return a+b; Sum s2 = new Sum((new Program()).Add2); 这句对应的IL如下: IL_0019: newobj instance void delegate_test.Program::.ctor() IL_001e: ldftn instance int32 delegate_test.Program::Add2(int32, int32) IL_0024: newobj instance void delegate_test.Sum::.ctor(object, native int) 委托的Invoke函数就是我们实际运行委托时调用的函数,至于BeginInvoke和EndInvoke则是用于异步的情况。 using System; namespace delegate_test internal delegate int Sum(int a, int b); class Program public static void Main(string[] args) Console.WriteLine("Hello World!"); // 实例化委托,这里和委托的IL代码中构造函数不一致 // IL代码中 Sum 的构造函数需要传入2个参数(一个object 类型,一个 native int类型) .method public hidebysig specialname rtspecialname instance void .ctor ( object 'object', native int 'method' ) runtime managed } // end of method Sum::.ctor Sum s = new Sum(Add); Sum s2 = new Sum((new Program()).Add2); // 调用委托的代码 s(5,7) 其实就是调用委托类的 Invoke方法 Console.WriteLine("5+7="+ s(5,7)); // 剥去语法外衣,也可以写成 Console.WriteLine("5+7="+ s.Invoke(5,7)); Console.Write("Press any key to continue . . . "); Console.ReadKey(true); public static int Add(int a, int b) return a+b; public int Add2(int a, int b) return a+b; s(5,7) 就是 s.Invoke(5,7) 的简化写法。 2.  委托链 委托既然是用于回调函数,那就应该可以一次回调多个函数,形成一个委托链。 这个委托链中应该还可以动态追加和减少回调函数。 实验代码如下: using System; namespace delegate_test internal delegate void Print_Sum(int a, int b); class Program public static void Main(string[] args) Console.WriteLine("Hello World!"); Print_Sum s = null; Print_Sum s1 = new Print_Sum(Add1); Print_Sum s2 = new Print_Sum((new Program()).Add2); Print_Sum s3 = new Print_Sum((new Program()).Add3); s += s1; s += s2; s += s3; s(1,2); // 委托链中删除Add2 s -= s2; s(1,2); Console.Write("Press any key to continue . . . "); Console.ReadKey(true); public static void Add1(int a, int b) Console.WriteLine("function Add1.... result is " + (a+b).ToString()); public void Add2(int a, int b) Console.WriteLine("function Add2.... result is " + (2*(a+b)).ToString()); public void Add3(int a, int b) Console.WriteLine("function Add3.... result is " + (3*(a+b)).ToString()); 运行的结果也预期相符,其实其中的 s += s1; 和 s -= s2; 也是C#的语法糖,实际调用的是System.Delegate的Combine方法和Remove方法。 感兴趣的话,可以看编译后的IL代码。 3. 动态委托 动态委托其实应用的并不多,它其实是和反射结合使用的。只有在你无法确定所调用的委托所定义的参数个数和参数类型时,才需要用到动态委托。 使用动态委托主要就是使用 生成委托实例的 CreateDelegate 方法和 调用委托的 DynamicInvoke 方法。 using System; using System.Reflection; internal delegate void print_sum(int a, int b); internal delegate void print_string(string a); public class Dynamic_Delegate public static void Main(string[] args) // 假设要调用的delegate名称,方法名称,方法的参数都是由 args传入的。 // Main方法中并不知道要调用哪个委托 // 调用委托print_sum时: // delegate_test.exe print_sum Sum 2 3 // 调用委托print_string时: // delegate_test.exe print_string Str "hello delegate!" // 获取各个参数 string delegate_name = args[0]; string method_name = args[1]; // 由于2种委托的参数不同,所以参数可能是一个,可能是两个 object[] method_args = new object[args.Length - 2]; for (int i = 0; i<args.Length-2; i++) // print_sum的参数需要转换成int型 if (delegate_name.Equals("print_sum")) method_args[i] = int.Parse(args[2+i]); method_args[i] = args[2+i]; // 获取委托类型 Type delegate_type = Type.GetType(delegate_name); // 获取方法信息 MethodInfo mi = typeof(Dynamic_Delegate).GetMethod(method_name, BindingFlags.NonPublic | BindingFlags.Static); // 根据获取的委托类型和方法信息创建一个delegate Delegate d = Delegate.CreateDelegate(delegate_type, mi); // 动态调用委托 d.DynamicInvoke(method_args); Console.Write("Press any key to continue . . . "); Console.ReadKey(true); private static void Sum(int a, int b) Console.WriteLine("delegate print_sum....... result is : " + (a+b).ToString()); private static void Str(string a) Console.WriteLine("delegate print_string.... result is : " + a); 根据编译成的程序集(我的程序集名称是delegate_test.exe),在命令行中分别输入如下命令来分别调用不同的delegate: delegate_test.exe print_sum Sum 2 3 delegate_test.exe print_string Str "hello delegate!"

《Linux内核设计与实现》读书笔记(十五)- 进程地址空间(kernel 2.6.32.60)

进程地址空间也就是每个进程所使用的内存,内核对进程地址空间的管理,也就是对用户态程序的内存管理。 主要内容: 地址空间(mm_struct) 虚拟内存区域(VMA) 地址空间和页表 1. 地址空间(mm_struct) 地址空间就是每个进程所能访问的内存地址范围。 这个地址范围不是真实的,是虚拟地址的范围,有时甚至会超过实际物理内存的大小。 现代的操作系统中进程都是在保护模式下运行的,地址空间其实是操作系统给进程用的一段连续的虚拟内存空间。 地址空间最终会通过页表映射到物理内存上,因为内核操作的是物理内存。 虽然地址空间的范围很大,但是进程也不一定有权限访问全部的地址空间(一般都是只能访问地址空间中的一些地址区间), 进程能够访问的那些地址区间也称为 内存区域。 进程如果访问了有效内存区域以外的内容就会报 “段错误” 信息。 内存区域中主要包含以下信息: - 代码段(text section),即可执行文件代码的内存映射 - 数据段(data section),即可执行文件的已初始化全局变量的内存映射 - bss段的零页(页面信息全是0值),即未初始化全局变量的内存映射 - 进程用户空间栈的零页内存映射 - 进程使用的C库或者动态链接库等共享库的代码段,数据段和bss段的内存映射 - 任何内存映射文件 - 任何共享内存段 - 任何匿名内存映射,比如由 malloc() 分配的内存 注:bss是 block started by symbol 的缩写。 linux中内存相关的概念稍微整理了一下,供参考: 进程映射的内存大小,这不是进程实际使用的内存大小 RSS(Resident set size) 实际驻留在“内存”中的内存大小,不包含已经交换出去的内存 SHARE RSS中与其他进程共享的内存大小 VMSIZE 进程占用的总地址空间,包含没有映射到内存中的页 Private RSS 仅由进程单独占用的RSS,也就是进程实际占用的内存 1.1 mm_struct介绍 linux中的地址空间是用 mm_struct 来表示的。 下面对其中一些关键的属性进行了注释,有些属性我也不是很了解...... struct mm_struct { struct vm_area_struct * mmap; /* [内存区域]链表 */ struct rb_root mm_rb; /* [内存区域]红黑树 */ struct vm_area_struct * mmap_cache; /* 最近一次访问的[内存区域] */ unsigned long (*get_unmapped_area) (struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags); /* 获取指定区间内一个还未映射的地址,出错时返回错误码 */ void (*unmap_area) (struct mm_struct *mm, unsigned long addr); /* 取消地址 addr 的映射 */ unsigned long mmap_base; /* 地址空间中可以用来映射的首地址 */ unsigned long task_size; /* 进程的虚拟地址空间大小 */ unsigned long cached_hole_size; /* 如果不空的话,就是 free_area_cache 后最大的空洞 */ unsigned long free_area_cache; /* 地址空间的第一个空洞 */ pgd_t * pgd; /* 页全局目录 */ atomic_t mm_users; /* 使用地址空间的用户数 */ atomic_t mm_count; /* 实际使用地址空间的计数, (users count as 1) */ int map_count; /* [内存区域]个数 */ struct rw_semaphore mmap_sem; /* 内存区域信号量 */ spinlock_t page_table_lock; /* 页表锁 */ struct list_head mmlist; /* 所有地址空间形成的链表 */ /* Special counters, in some configurations protected by the * page_table_lock, in other configurations by being atomic. mm_counter_t _file_rss; mm_counter_t _anon_rss; unsigned long hiwater_rss; /* High-watermark of RSS usage */ unsigned long hiwater_vm; /* High-water virtual memory usage */ unsigned long total_vm, locked_vm, shared_vm, exec_vm; unsigned long stack_vm, reserved_vm, def_flags, nr_ptes; unsigned long start_code, end_code, start_data, end_data; /* 代码段,数据段的开始和结束地址 */ unsigned long start_brk, brk, start_stack; /* 堆的首地址,尾地址,进程栈首地址 */ unsigned long arg_start, arg_end, env_start, env_end; /* 命令行参数,环境变量首地址,尾地址 */ unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */ struct linux_binfmt *binfmt; cpumask_t cpu_vm_mask; /* Architecture-specific MM context */ mm_context_t context; /* Swap token stuff */ * Last value of global fault stamp as seen by this process. * In other words, this value gives an indication of how long * it has been since this task got the token. * Look at mm/thrash.c unsigned int faultstamp; unsigned int token_priority; unsigned int last_interval; unsigned long flags; /* Must use atomic bitops to access the bits */ struct core_state *core_state; /* coredumping support */ #ifdef CONFIG_AIO spinlock_t ioctx_lock; struct hlist_head ioctx_list; #endif #ifdef CONFIG_MM_OWNER * "owner" points to a task that is regarded as the canonical * user/owner of this mm. All of the following must be true in * order for it to be changed: * current == mm->owner * current->mm != mm * new_owner->mm == mm * new_owner->alloc_lock is held struct task_struct *owner; #endif #ifdef CONFIG_PROC_FS /* store ref to file /proc/<pid>/exe symlink points to */ struct file *exe_file; unsigned long num_exe_file_vmas; #endif #ifdef CONFIG_MMU_NOTIFIER struct mmu_notifier_mm *mmu_notifier_mm; #endif 补充说明1: 上面的属性中,mm_users 和 mm_count 很容易混淆,这里特别说明一下:(下面的内容有网上查找的,也有我自己理解的) mm_users 比较好理解,就是 mm_struct 被用户空间进程(线程)引用的次数。 如果进程A中创建了3个新线程,那么 进程A(这时候叫线程A也可以)对应的 mm_struct 中的 mm_users = 4 补充一点,linux中进程和线程几乎没有什么区别,就是看它是否共享进程地址空间,共享进程地址空间就是线程,反之就是进程。 所以,如果子进程和父进程共享了进程地址空间,那么父子进程都可以看做线程。如果父子进程没有共享进程地址空间,就是2个进程 mm_count 则稍微有点绕人,其实它记录就是 mm_struct 实际的引用计数。 简单点说,当 mm_users=0 时,并不一定能释放此 mm_struct,只有当 mm_count=0 时,才可以确定释放此 mm_struct 从上面的解释可以看出,可能引用 mm_struct 的并不只是用户空间的进程(线程) 当 mm_users>0 时, mm_count 会增加1, 表示有用户空间进程(线程)在使用 mm_struct。不管使用 mm_struct 的用户进程(线程)有几个, mm_count 都只是增加1。 也就是说,如果只有1个进程使用 mm_struct,那么 mm_users=1,mm_count也是 1。 如果有9个线程在使用 mm_struct,那么 mm_users=9,而 mm_count 仍然为 1。 那么 mm_count 什么情况下会大于 1呢? 当有内核线程使用 mm_struct 时,mm_count 才会再增加 1。 内核线程为何会使用用户空间的 mm_struct 是有其他原因的,这个后面再阐述。这里先知道内核线程使用 mm_struct 时也会导致 mm_count 增加 1。 在下面这种情况下,mm_count 就很有必要了: - 进程A启动,并申请了一个 mm_struct,此时 mm_users=1, mm_count=1 - 进程A中新建了2个线程,此时 mm_users=3, mm_count=1 - 内核调度发生,进程A及相关线程都被挂起,一个内核线程B 使用了进程A 申请的 mm_struct,此时 mm_users=3, mm_count=2 - CPU的另一个core调度了进程A及其线程,并且执行完了进程A及其线程的所有操作,也就是进程A退出了。此时 mm_users=0, mm_count=1   在这里就看出 mm_count 的用处了,如果只有 mm_users 的话,这里 mm_users=0 就会释放 mm_struct,从而有可能导致 内核线程B 异常。 - 内核线程B 执行完成后退出,这时 mm_users=0,mm_count=0,可以安全释放 mm_struct 了 补充说明2:为何内核线程会使用用户空间的 mm_struct? 对Linux来说,用户进程和内核线程都是task_struct的实例, 唯一的区别是内核线程是没有进程地址空间的(内核线程使用的内核地址空间),内核线程的mm描述符是NULL,即内核线程的tsk->mm域是空(NULL)。 内核调度程序在进程上下文的时候,会根据tsk->mm判断即将调度的进程是用户进程还是内核线程。 但是虽然内核线程不用访问用户进程地址空间,但是仍然需要页表来访问内核自己的空间。 而任何用户进程来说,他们的内核空间都是100%相同的,所以内核会借用上一个被调用的用户进程的mm_struct中的页表来访问内核地址,这个mm_struct就记录在active_mm。 简而言之就是,对于内核线程,tsk->mm == NULL表示自己内核线程的身份,而tsk->active_mm是借用上一个用户进程的mm_struct,用mm_struct的页表来访问内核空间。 对于用户进程,tsk->mm == tsk->active_mm。 补充说明3:除了 mm_users 和 mm_count 之外,还有 mmap 和 mm_rb 需要说明以下: 其实 mmap 和 mm_rb 都是保存此 进程地址空间中所有的内存区域(VMA)的,前者是以链表形式存放,后者以红黑树形式存放。 用2种数据结构组织同一种数据是为了便于对VMA进行高效的操作。 1.2 mm_struct操作 1. 分配进程地址空间 参考 kernel/fork.c 中的宏 allocate_mm #define allocate_mm() (kmem_cache_alloc(mm_cachep, GFP_KERNEL)) #define free_mm(mm) (kmem_cache_free(mm_cachep, (mm))) 其实分配进程地址空间时,都是从slab高速缓存中分配的,可以通过 /proc/slabinfo 查看 mm_struct 的高速缓存 # cat /proc/slabinfo | grep mm_struct mm_struct 35 45 1408 5 2 : tunables 24 12 8 : slabdata 9 9 0 2. 撤销进程地址空间 参考 kernel/exit.c 中的 exit_mm() 函数 该函数会调用 mmput() 函数减少 mm_users 的值, 当 mm_users=0 时,调用 mmdropo() 函数, 减少 mm_count 的值, 如果 mm_count=0,那么调用 free_mm 宏,将 mm_struct 还给 slab高速缓存 3. 查看进程占用的内存: cat /proc/<PID>/maps pmap PID 2. 虚拟内存区域(VMA) 内存区域在linux中也被称为虚拟内存区域(VMA),它其实就是进程地址空间上一段连续的内存范围。 2.1 VMA介绍 VMA的定义也在 <linux/mm_types.h> 中 struct vm_area_struct { struct mm_struct * vm_mm; /* 相关的 mm_struct 结构体 */ unsigned long vm_start; /* 内存区域首地址 */ unsigned long vm_end; /* 内存区域尾地址 */ /* linked list of VM areas per task, sorted by address */ struct vm_area_struct *vm_next, *vm_prev; /* VMA链表 */ pgprot_t vm_page_prot; /* 访问控制权限 */ unsigned long vm_flags; /* 标志 */ struct rb_node vm_rb; /* 树上的VMA节点 */ * For areas with an address space and backing store, * linkage into the address_space->i_mmap prio tree, or * linkage to the list of like vmas hanging off its node, or * linkage of vma in the address_space->i_mmap_nonlinear list. union { struct { struct list_head list; void *parent; /* aligns with prio_tree_node parent */ struct vm_area_struct *head; } vm_set; struct raw_prio_tree_node prio_tree_node; } shared; * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma * list, after a COW of one of the file pages. A MAP_SHARED vma * can only be in the i_mmap tree. An anonymous MAP_PRIVATE, stack * or brk vma (with NULL file) can only be in an anon_vma list. struct list_head anon_vma_node; /* Serialized by anon_vma->lock */ struct anon_vma *anon_vma; /* Serialized by page_table_lock */ /* Function pointers to deal with this struct. */ const struct vm_operations_struct *vm_ops; /* Information about our backing store: */ unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */ struct file * vm_file; /* File we map to (can be NULL). */ void * vm_private_data; /* was vm_pte (shared mem) */ unsigned long vm_truncate_count;/* truncate_count or restart_addr */ #ifndef CONFIG_MMU struct vm_region *vm_region; /* NOMMU mapping region */ #endif #ifdef CONFIG_NUMA struct mempolicy *vm_policy; /* NUMA policy for the VMA */ #endif 这个结构体各个字段的英文注释都比较详细,就不一一翻译了。 上述属性中的 vm_flags 标识了此VM 对 VMA和页面的影响: vm_flags 的宏定义参见 <linux/mm.h> 对VMA及其页面的影响 VM_READ 页面可读取 VM_WRITE VM_EXEC 页面可执行 VM_SHARED 页面可共享 VM_MAYREAD VM_READ 标志可被设置 VM_MAYWRITER VM_WRITE 标志可被设置 VM_MAYEXEC VM_EXEC 标志可被设置 VM_MAYSHARE VM_SHARE 标志可被设置 VM_GROWSDOWN 区域可向下增长 VM_GROWSUP 区域可向上增长 VM_SHM 区域可用作共享内存 VM_DENYWRITE 区域映射一个不可写文件 VM_EXECUTABLE 区域映射一个可执行文件 VM_LOCKED 区域中的页面被锁定 VM_IO 区域映射设备I/O空间 VM_SEQ_READ 页面可能会被连续访问 VM_RAND_READ 页面可能会被随机访问 VM_DONTCOPY 区域不能在 fork() 时被拷贝 VM_DONTEXPAND 区域不能通过 mremap() 增加 VM_RESERVED 区域不能被换出 VM_ACCOUNT 该区域时一个记账 VM 对象 VM_HUGETLB 区域使用了 hugetlb 页面 VM_NONLINEAR 该区域是非线性映射的 2.2 VMA操作 vm_area_struct 结构体定义中有个 vm_ops 属性,其中定义了内核操作 VMA 的方法 * These are the virtual MM functions - opening of an area, closing and * unmapping it (needed to keep files on disk up-to-date etc), pointer * to the functions called when a no-page or a wp-page exception occurs. struct vm_operations_struct { void (*open)(struct vm_area_struct * area); /* 指定内存区域加入到一个地址空间时,该函数被调用 */ void (*close)(struct vm_area_struct * area); /* 指定内存区域从一个地址空间删除时,该函数被调用 */ int (*fault)(struct vm_area_struct *vma, struct vm_fault *vmf); /* 当没有出现在物理页面中的内存被访问时,该函数被调用 */ /* 当一个之前只读的页面变为可写时,该函数被调用, * 如果此函数出错,将导致一个 SIGBUS 信号 */ int (*page_mkwrite)(struct vm_area_struct *vma, struct vm_fault *vmf); /* 当 get_user_pages() 调用失败时, 该函数被 access_process_vm() 函数调用 */ int (*access)(struct vm_area_struct *vma, unsigned long addr, void *buf, int len, int write); #ifdef CONFIG_NUMA * set_policy() op must add a reference to any non-NULL @new mempolicy * to hold the policy upon return. Caller should pass NULL @new to * remove a policy and fall back to surrounding context--i.e. do not * install a MPOL_DEFAULT policy, nor the task or system default * mempolicy. int (*set_policy)(struct vm_area_struct *vma, struct mempolicy *new); * get_policy() op must add reference [mpol_get()] to any policy at * (vma,addr) marked as MPOL_SHARED. The shared policy infrastructure * in mm/mempolicy.c will do this automatically. * get_policy() must NOT add a ref if the policy at (vma,addr) is not * marked as MPOL_SHARED. vma policies are protected by the mmap_sem. * If no [shared/vma] mempolicy exists at the addr, get_policy() op * must return NULL--i.e., do not "fallback" to task or system default * policy. struct mempolicy *(*get_policy)(struct vm_area_struct *vma, unsigned long addr); int (*migrate)(struct vm_area_struct *vma, const nodemask_t *from, const nodemask_t *to, unsigned long flags); #endif 除了以上的操作之外,还有一些辅助函数来方便内核操作内存区域。 这些辅助函数都可以在 <linux/mm.h> 中找到 1. 查找地址空间 /* Look up the first VMA which satisfies addr < vm_end, NULL if none. */ extern struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr); extern struct vm_area_struct * find_vma_prev(struct mm_struct * mm, unsigned long addr, struct vm_area_struct **pprev); /* Look up the first VMA which intersects the interval start_addr..end_addr-1, NULL if none. Assume start_addr < end_addr. */ static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr) struct vm_area_struct * vma = find_vma(mm,start_addr); if (vma && end_addr <= vma->vm_start) vma = NULL; return vma; 2. 创建地址区间 static inline unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flag, unsigned long offset) unsigned long ret = -EINVAL; if ((offset + PAGE_ALIGN(len)) < offset) goto out; if (!(offset & ~PAGE_MASK)) ret = do_mmap_pgoff(file, addr, len, prot, flag, offset >> PAGE_SHIFT); return ret; 3. 删除地址区间 extern int do_munmap(struct mm_struct *, unsigned long, size_t); 3. 地址空间和页表 地址空间中的地址都是虚拟内存中的地址,而CPU需要操作的是物理内存,所以需要一个将虚拟地址映射到物理地址的机制。 这个机制就是页表,linux中使用3级页面来完成虚拟地址到物理地址的转换。 1. PGD - 全局页目录,包含一个 pgd_t 类型数组,多数体系结构中 pgd_t 类型就是一个无符号长整型 2. PMD - 中间页目录,它是个 pmd_t 类型数组 3. PTE - 简称页表,包含一个 pte_t 类型的页表项,该页表项指向物理页面 虚拟地址 - 页表 - 物理地址的关系如下图: