2014年4月

Yii:zii.widgets.CMenu使用方法

Yii框架中的一个生成菜单的小物件。

先上个示例代码:

<?php
$this->widget('zii.widgets.CMenu',array(
    'activeCssClass'=>'当前热点元素的样式',
    'firstItemCssClass'=>'第一个元素的样式',
    'lastItemCssClass'=>'最后一个元素的样式',
    'encodeLable'=>'false',  //当值为false时,label标签中的html就会将样式显示出来.
    'htmlOptions'=>array('class'=>'默认样式'),

    'items'=>array(
        array('label'=>'网站概况', 'url'=>array('/admin'),'itemOptions'=>array('class'=>'li_status'),'active'=>$this->id=='admin'?true:false),
        array('label'=>'图片管理', 'url'=>array('/picture'),'template'=>'{menu}<span>this is additional infomation</span>','itemOptions'=>array('class'=>'li_picture'),'active'=>$this->id=='picture'?true:false, 'visible'=>true),
        array('label'=>'管理员管理', 'url'=>array('/manager'),'itemOptions'=>array('class'=>'li_manager'),'submenuOptions'=>array('class'=>'subclass'),'active'=>($this->id=='manager' && $this->action->id!='changepswd')?true:false, 'visible'=>false),
        array('label'=>'密码修改', 'url'=>array('/manager/changepswd'),'linkOptions'=>array('target'=>'_blank'),'itemOptions'=>array('class'=>'li_changepswd'),'items'=>array(array('label'=>'子栏目'))),'active'=>($this->id=='manager' && $this->action->id=='changepswd')?true:false, 'visible'=>true),
        array('label'=>'登陆', 'url'=>array('/site/login'),'itemOptions'=>array('class'=>'li_login'), 'visible'=>Yii::app()->user->isGuest),
        array('label'=>'退出 ('.Yii::app()->user->name.')', 'url'=>array('/site/logout'),'itemOptions'=>array('class'=>'li_login'), 'visible'=>!Yii::app()->user->isGuest)
    ),
));

?>

在模板中使用上面的代码,最终会生成以<ul><li></li></ul>构成的菜单列表,针对item中的每一个数组,可以进行以下设置:

label:菜单显示的文本,可以加html进行修饰,但要将encodeLabel参数值设为false

url:链接地址,若是字符串,则是基于网站根地址的绝对路径,比如网站地址为veitor.net,字符串url设置为"article",则最终生成的地址为veitor.net/article,如果设置类型为数组,则效果与createUrl方法一样,比如网址还是veitor.net,设置的数组url为"array(detail/article)",则最终生成的地址为veitor.net/?r=detail/article,控制器/方法格式的

visible:可见,boolean值,当然可以用函数来取值,决定什么情况下隐藏

active:正在访问,boolean值,如果是true,会在相应li中加入active样式,上面代码用到$this->id是个很好用的方法

items:定义子目录,array,通过样式可定义收缩排列或者鼠标经过时显示子目录

template:模板,模板中用{menu}来代表替换内容,见上代码

linkOptions:<a>的属性,可定义class,rel,target等属性,见上代码

itemOptions:<li>的属性,可定义class等属性,见上代码

submenuOptions:子栏目的<ul>属性,<li>和<a>属性还是和上面一样分别对item设置

activeCssClass:当前选中菜单的css的Class名称

firstItemCssClass:第一个菜单按钮的Css的Class名称

lastItemCssClass:最后一个菜单按钮的Css的Class名称

当然可以分别为每个Item菜单元素添加指定的Class,即在对应的Item元素上增加itemOptions设置(看上面代码)

 

这些就是我用CMenu的一些见解,如果你有更好的方法可以一起交流。

Yii:render渲染视图分析

一起了解一下render方法的工作原理,先看render代码:

public function render($view,$data=null,$return=false)
	{
		if($this->beforeRender($view))
		{
			$output=$this->renderPartial($view,$data,true);
			if(($layoutFile=$this->getLayoutFile($this->layout))!==false)
				$output=$this->renderFile($layoutFile,array('content'=>$output),true);

			$this->afterRender($view,$output);

			$output=$this->processOutput($output);

			if($return)
				return $output;
			else
				echo $output;
		}
	}

首先是beforeRender:

protected function beforeRender($view)
{
	return true;
}

这个封装方法是用在render方法开头,参数是模板文件名称(不带后缀),是用来在渲染视图之前做的一些预处理,我们可以在控制器里重写这个方法来处理,但结果返回的值必须等于true,以便输出视图。

接着执行renderPartial方法:

public function renderPartial($view,$data=null,$return=false,$processOutput=false)
	{
		if(($viewFile=$this->getViewFile($view))!==false)
		{
			$output=$this->renderFile($viewFile,$data,true);
			if($processOutput)
				$output=$this->processOutput($output);
			if($return)
				return $output;
			else
				echo $output;
		}
		else
			throw new CException(Yii::t('yii','{controller} cannot find the requested view "{view}".',
				array('{controller}'=>get_class($this), '{view}'=>$view)));
	}

这个方法是$view为模板文件名(不带后缀,后缀通过配置指定),$data是一个关联数组,它将被extract函数提取成供模板中使用的PHP变量。这个方法与render区别在于,该方法渲染视图时,不会渲染布局也不会加载Yii内置的js\css,而render会一起渲染布局文件和脚本。

其中的getViewFile方法是根据模板名寻找木板文件的方法,如果在main配置文件中指定了主题,那么确定的一个模板将被找到。否则的话将按一下规则寻找:

模块内的视图:直接指定模板文件路径,以单个斜线“\”开始的路径。

应用内的视图:直接指定模板文件路径,以双斜线“\\”开始的路径。

别名路径:以点分割的路径。别名路径alias以后会讲,其实在初始化应用的时候定义了一些路径别名。

这里只需了解getViewFile是寻找模板地址和extract变量即可,接着讲renderPartial,找到模板后继续执行。接着要执行到判断第四个参数processOutput,该函数主要是用于是否加载Yii内置js\css,即上面刚才说的render和renderPartial区别之一,在render中该参数为true即加载,在renderPartial中反之。

然后接着执行到getLayoutFile,刚才是获取了不带布局视图,相当于只执行了renderPartial方法,这里再获取布局文件。getLayoutFile是查找布局模板文件,查找方法和上面的查找模板文件类似。通过在控制器中设置layout变量,加载应用内的布局文件的话设置路径只要以双斜线“\\”开头即可,加载模块内的布局路径以单个斜线“\”开头,另外还有别名路径同样可以设置。

找到布局文件之后,依然使用renderFile方法渲染布局文件,将之前渲染的试图文件插入到布局文件中。

接着执行afterRender方法,也不需要太多解释了,当然是执行渲染之后的一些工作,可以自己定义。

再执行processOutput,因为render是默认加载js\css的,所以这里不需要判断了。

最后再根据render第三个参数判断是否要直接输出,如果为false的话,就是直接看到的页面了,为true只将html代码返回。

 

 

render的方法基本就是这样了,主要还是在renderPartial里处理过程比较多。如果有疑问,可以在下面留言给我,一起探讨。

所有编程皆为Web编程

大部分人想去做Web编程的原因是,他们不够聪明,因此也做不了别的事。他们不懂编译器、并发性、3D或类继承。他们根本不明白我为什么要使用接口或者抽象类。他们不理解虚函数、指针、引用、垃圾回收、终结器、传引用与传值的区别、C++的虚拟析构函数、或者C#的结构体与类之间的差别。他们对进程也一无所知。更别提瀑布、螺旋、敏捷了!他们从来没看过需求文档,也从来没写过设计文档;他们从没画过一张UML图,甚至听都没听说过有“顺序图”这种东西。

  不过,他们确实有些手段:他们知道怎样匆匆拼凑一个ASP.NET网页,向数据库发一些(写得很糟糕的)SQL指令,填上一个数据集,然后用网格控件展示出来。他们也就会这些了吧。而且,他们在弄明白这些东西的时候,很可能也没费多长时间。

  因此,请恕我冒犯——我才不想当Web程序员呢!我有两个理由。第一,那对我太没有挑战性了。第二,因为绝大多数互联网公司的工程师都很糟糕,更准确地说,因为你不必去琢磨深奥的东西就能成为一个Web开发者。在我看来,互联网正让我们变得越来越弱智。拼凑出一个网页真的不需要你有那么聪明。

  我真心希望大家都错了,希望不必所有的东西都“转向Web”。因为如果真这样的话,将来的某一天,我要么会不情愿地接受这种无聊的转变,要么只能换一个职业了。

Web开发没有挑战性,所以吸引了不够格的软件开发者?让我们姑且不讨论这种荒谬的观点。即使过去真是这样,如今的情况也已经不同了。

我其实不想在Michael面前扮演“坏人”,告诉他这个坏消息:对于越来越多的用户来说,桌面应用软件已经完蛋了。几年来,大部分桌面应用软件都被Web应用代替了。随着网络浏览器进化得越来越强大和健壮,每天都有更多的桌面应用被取代。

你希望不必所有的东西都“转向Web”?醒醒吧!这已经发生了!

任何学习计算机历史的学生都可以告诉你,Web应用成为主流正是“最不强大原则”(The Principle of Least Power)所预示的:

 计算机科学界过去花了40年的时间,致力于让语言尽可能地强大。时至今日,我们必须感激那些让我们选择不是最强大的解决方案(而选择最不强大的方案)的原因。计算机语言越不强大,你对用那种语言存储的数据能做的事情就越多。如果你以一种简单的陈述形式来书写,任何人都可以写一个程序去分析它。比方说,一个呈现天气信息的网页使用RDF(译者注:资源描述框架,一种用于描述Web资源的标记语言)来描述数据,用户可以把它装进一个表格,也许再做一下平均计算,绘制图表,然后结合其他信息做出某种推断。另一种极端的做法是,天气信息通过巧妙的Java小程序描绘出来。尽管这种方式可能做出很酷的用户界面,但它完全不能被分析。找到这个页面的搜索引擎将无法判断那是什么数据或者那些数据有什么用。要想知道一个Java小程序是做什么的,唯一的办法是亲眼目睹它运行起来。

互联网恰恰是做最简单(或者说最傻)而靠谱之事的具体表现。如果这吓到了你(让你感到不安了),那么我要小声地告诉你,你没有理由成为一名程序员。

所有应用都应该成为Web应用吗?当然不是。总有一些重要的例外,有些种类的软件跟网络也毫无关系。但是,这些是少数情况,是一些特殊应用。它们固然是重要的小生态环境,但不管怎么说,就只是“小生态”。

如果你希望尽可能多的用户来使用你的软件,绝没有比把它做成Web应用更好的方法了。对于现存软件来说,互联网是最高效、最普遍、最直接的分发网络。任何用户只要能连上网,有一个浏览器,不管他身处世界的任何一个角落,只须点两下鼠标就能与你写的软件开始交互了。哪怕是最蹩脚的Web应用,它的受众面和传播度都是令人震惊的;而且还在每天扩大着……我也曾因此杜撰了“阿特伍德定律”:任何可以用JavaScript来写的应用,最终都将用JavaScript来写。

从工程的角度来看,用JavaScript来写Photoshop、Word或Excel毫无意义。但是,这是不可避免的。这事会发生的。实际上,这事已经在发生了。环顾一下你的四周吧。

作为一名软件开发者,最让我开心的是编写有人使用的软件。如果你的软件委身于一个二进制的EXE文件,它必须被购买、授权、运送、下载、安装、维护和升级,你的技艺又有什么意义呢?考虑到程序员与用户之间的所有这些传统障碍,软件行业居然还能生存下来,这真是一个奇迹!然而,在华丽而崭新的Web应用世界里,那些制约已经不复存在。边界没有了。软件可以无处不在!

Web编程还远远没有达到完美的境地。其实,还有点乱!没错,随便会写点代码的人就能三下两下地搞出一个糟糕的Web应用;也确实,99%的Web应用都似狗屎一堆。但是,这也意味着,相当“聪明”的程序员们正在将他们的成果展现在成百上千(或者成千上万,甚至几百万)的用户面前,而这在互联网盛行之前是绝无可能的。在我看来,让代码遭受冷落、让它们就此孤独终老,没什么比这更让人感到悲哀的了!把软件按照Web应用的形式重整一下,即使软件本身并不怎么样,这也使得程序员们能够把他们的软件展现在某个地方的某人面前。

如果受众面和技艺方面的观点不足以说服你,那就从商业的角度来考虑一下吧。Mark Fletcher曾经给出过一份创业规则,其中就有这么一条:

 你在做一个Web应用,对吧?这不是20世纪80年代了!纵然你的竞争对手把(桌面)应用软件做得别致优雅,你那简陋而寒酸的Web应用也仍然会比它更加成功。

要不了多久,所有编程都将是Web编程。对于普通的在职程序员来说,如果你不认为这是一个值得庆贺的理由,你也许应该转行了。

原文地址:http://blog.codinghorror.com/all-programming-is-web-programming/

概念解释:集群与分布式的区别

之前一直对集群与分布式的概念不是很清楚,认为两个名词概念都差不多,那到底区别在哪呢,我也在网上找了很多解释,但似乎都没有言简意赅的讲清区别,或者说没有很形象的讲明每个名词是什么意思,到底有何用。那么我就依我的看法来讲一下,首先是分别讲解,再来合起来讲一下:

【集群】

处理同一个业务,目的是实现高可用性,可用性顾名思义,就是要让用户能用,并且随时能访问。一个集群针对一个业务并且有着多台服务器,当一个服务器挂掉,剩下的几台能顶替着继续工作(当然这几台服务器在那台服务器没挂的时候也不是闲着的,他们都是在同时处理大量请求的业务,那台服务器挂掉后,它的一些任务只好让这几台来顺带帮着完成了),这样用户能继续访问网站的某一业务。举个例子,比如我有发布日志的业务,如果我只有一台服务器支撑着这个业务,当服务器因某种原因挂掉后,那么用户也就无法发日志,这一业务也就无法使用。然而如果我有着服务器集群,其中一台挂掉,用户仍然能发日志。

【分布式】

将不同的业务放在不同的地方(节点),换句话说,每个分布式节点都处理不同的任务(一个节点处理一个业务),举个例子,有一个节点的业务是处理上传图片,还有一个节点的业务是处理发布文章,就这样把每个业务放在不同的地方(节点)。

【分布式与集群的关系】

每一个分布式节点都可以看做一个集群,上面说了,分布式的每一个节点都处理一种业务,而集群是处理同一业务,那么分布式的节点可以做一个集群。


 

当然,从不同角度看,可能理解也有偏差。主要还是集群的概念比较重要。我个人认为集群除了实现高可用性,还有高并发和高性能。这可能要涉及负载均衡了,比如访问一个网站的一个业务,对应的有一个集群,前面放着相应服务器,后面几台服务器负责处理这个业务,当有业务访问的时候,相应服务器会根据哪台服务器负载情况,将业务交给负载不是很重的服务器去完成

 

PHP程序员突破成长瓶颈

先明确我所指的PHP工程师,是指毕业工作后,主要以PHP进行WEB系统的开发,没有使用其他语言工作过。工作经验大概在3~4年,普通的WEB系统(百万级访问,千成级数据以内或业务逻辑不是特别复杂)开发起基本得心应手,没有什么问题。但他们会这样的物点:

除了PHP不使用其它的语言,可能会点shell 脚本。

对PHP的掌握不精(很多PHP手册都没有看完,库除外)

知识面比较窄(面对需求,除开使用PHP和mysql ,不知道其它的解决办法)

PHP代码以过程为主,认为面向对象的实现太绕,看不懂

这些PHPer 在遇到需要高性能,处理高并发,大量数据的项目或业务逻辑比较复杂(系统需要解决多领域业务的问题)时,缺少思路。不能分析问题的本质,技术判断力比较差,对于问题较快能找出临时的解决办法,但常常在不断临时性的解决办法中,系统和自己一步步走向崩溃。那怎么提高自己呢?怎么可以挑战难度更高的系统?

更高的挑战在那里?

结合我自己的经验,我列出一些具体挑战,让大家先有个感性的认识。

高性能系统的挑战在哪里?

如何选择WEB服务器?要不要使用fast-cgi 模式

要不要使用反向代理服务?选择全内存缓存还是硬盘缓存?

是否需要负载均衡?是基于应用层,还是网络层? 如何保证高可靠性?

你的PHP代码性能如何,使用优化工具后怎么样? 性能瓶颈在那里? 是否需要写成C的扩展?

用户访问有什么特点,是读多还是写多?是否需要读写分离?

数据如何存储?写入速度和读出速度如何? 数据增涨访问速读如何变化?

如何使用缓存? 怎么样考虑失效?数据的一致性怎么保证?

高复杂性系统的挑战在哪里?

能否识别业务所对应的领域?是一个还是多个?

能否合理对业务进行抽象,在业务规则变化能以很小的代价实现?

数据的一致性、安全性可否保证?

是否撑握了面向对象的分析和设计的方法

当我所列出的问题,你都能肯定的回答,我想在技术上你基本已经可能成为架构师了。如何你还不能回答,你需要在以下几个方向加强。

怎么样提高,突破瓶颈

如何你还不能回答,你需要在以下几个方向加强:

分析你所使用的技术其原理和背后运行的机制,这样可以提高你的技术判断力,提高你技术方案选择的正确性;
学习大学期间重要的知识, 操作系统原理,数据结构和算法。知道你以前学习都是为了考试,但现在你需要为自己学习,让自己知其所以然。

重新开始学习C语言,虽然你在大学已经学过。这不仅是因为你可能需要写PHP扩展,而且还因为,在做C的应用中,有一个时刻关心性能、内存控制、变量生命周期、数据结构和算法的环境。

学习面向对象的分析与设计,它是解决复杂问题的有效的方法。学习抽象,它是解决复杂问题的唯一之道。

“这么多的东西怎么学,这得学多久呀” ?

如果你努力的话,有较好的规划,估计需要1~2年的时间,怎么学习的问题,我们后续再谈。

(注:下面是原文作者左文建分享的学习方法)

学习建议

如何有效的学习是一个大问题。 自己有些实践但很零散,不好总结。昨天晚上睡觉前,突然想到了RUP的核心,“以架构为中心,用例驱动,迭代开发”,借用这个思想,关于有效的学习的方法,可以这样来表述:

以原理、模型或机制为中心,任务驱动,迭代学习

有点抽象, 举个例子来说明如何学习。

目的: 学习如何提高处理性能。

可迭代驱动的任务: 通过IP找到所在地域。

这是WEB应用常见的任务,IP数据库是10左右万行的记录。

第一次迭代: 不考虑性能的情况下实现功能(通过PHP来实现)

因为无法直接通过KEY(IP)进行查找地域,所以直接放到数据或通过关联数组这种简单的方法都是不行的。思路还是先把数据进行排序,然后再进行查找

1. 如何通过IP查找? 已序的数据,二分查找是最快的。

2. 如何排序?用库函数sort当然 是可以,但是即然是学习,那还是自己实现快速排序吧。

学习目标: 排序算法,查找算法

PHPer 数据结构和算法基础比较差,平时也没有这方面的任务,自己也不学习,因此这方面的知识很缺乏。但是,编程解决的问题,最终都会归结到数据结构和对这种数据结构操作的算法。如果数据结构算法常在心中,那遇到问题就能清晰认识到它内在的结构,解决方法就会自然产生。

第二次迭代:优化数据的加载与排序

如果做到第一步,那基本上还是不可用,因为数据每次都需要的加载和排序,这样太耗时间。 解决的思路是,数据一次加载排序后,放到每个PHP进程能访问到的地方。

放到memcache 这是大家容易想到问题。其实放到共享内存(EA等加速器都支持)中是更快的方式,因为memcache还多了网络操作。 数据是整体放入到共享内存,还是分块放入,如何测试性能? 如何分析瓶颈所在(xdebug)? 在这些问题的驱动下你会学习到

学习目标: 检测、定位、优化PHP性能的方法; PHP实现结构对性能的影响。

第三次迭代: 编写PHP的扩展

性能还是上不去,不得不进入C/C++的世界了,不过从此你将不只是PHPer 而服务端的全能型工程师,当然这对没有做过C/C++的同学挑战是巨大的。 我这里无法再简单来说如何学习C/C++ ,可以参看 《PHP程序员学习C++》

学习目标:C/C++的学习,PHP扩展的编写

怎么确定需要学习的机制和原理呢? 怎么找到驱动学习任务呢?

我对需要学习的东西,都没有什么概念,怎么回答以上的两个问题?

从这个技术的定位来找出需要学习的重点,即它怎么做到(机制)的和它为什么能这样做到 (模型或原理)
列出这个技术最常见的应用,做为学习的任务,从简到难进行实践。

假如我需要学习Javascript ,我对于HTML,CSS有点感性认识

首要我了解到,JS 是WEB领域的动态语言,主要解决网页的动态交互的。

那我要学习的要点如下:

JS如何与HTML 进行交互 (机制)

JS的动态特性在那里,与其它动态语言有何区别?(语言模型)

如果完全自学,找到需要学习的要点(机制、模型、原理) 设定学习任务的确不是那么容易把握。如果找到一个有经验的人来指导你或加一个学习型的团队,那学习的速度的确会大大提高。

最后,我想说的是: PHP因为简单而使用,但不能因为它的简单而限制我们成长!

获取PHP脚本文件路径的几个方法

别的不扯,直接正题。

在PHP中获得所执行脚本文件路径有以下几个方法:

  1. 魔术常量__FILE__:这个魔术常量使用率非常高,经常在定义目录的绝对路径时用dirname(__FILE__)这种形式。另一种绝对路径定义见这篇博文《魔术常量__DIR__和__FILE__的用法区别》,但是使用场合与下面两种有区别,使用该常量最终返回的都是包含的文件(这句话的意思就看下面的示例代码吧)。
  2. 使用两个全局变量字符串连接$_SERVER['DOCUMENT_ROOT'] . $SERVER['PHP_SELF']:两个变量拼接而成,前一个变量是获取根目录完整路径,后一个变量是获取从网站根目录到该执行文件的绝对路径。拼接后即是所需要的脚本文件完整路径。
  3. 依然是全局变量$_SERVER['SCRIPT_FILENAME']:这个变量效果和第2种类似

示例代码:

index.php文件,存放在e:/project/文件夹下,其中include了同级目录中的test1.php和子目录test中的test2.php

<?php

include './test1.php';
include './test/test2.php';

echo "<br>";
echo __FILE__;
echo "<br>";
echo $_SERVER['DOCUMENT_ROOT'].$_SERVER['PHP_SELF'];
echo "<br>";
echo $_SERVER['SCRIPT_FILENAME'];
echo "<br>";

?>

./test.1php和./test/test2.php文件代码相同。

<?php

echo "<br>";
echo __FILE__;
echo "<br>";
echo $_SERVER['DOCUMENT_ROOT'].$_SERVER['PHP_SELF'];
echo "<br>";
echo $_SERVER['SCRIPT_FILENAME'];
echo "<br>";

?>

接下来看一下结果:

E:\project\test1.php
E:/project/index.php
E:/project/index.php

E:\project\test\test2.php
E:/project/index.php
E:/project/index.php

E:\project\index.php
E:/project/index.php
E:/projectt/index.php

你会发现结果第5行有个不同处,该行就是输出的__FILE__,并且代码是在子目录test中test2.php文件里的。也就是说明__FILE__写在哪个文件里,输出结果就是显示哪个文件。而其他两个全局变量$_SERVER输出的结果都是最终包含的文件,在这个示例中也就是index.php文件(如果不明白这句话意思,看前面红色的文字,意思就是和它相反的)。

如果对本文有疑问,欢迎留言,一起探讨。

魔术常量__DIR__和__FILE__的用法区别

在写PHP脚本的时候,要定义一个文件目录的绝对路径,通常会使用这两种PHP内置的魔术方法:__DIR__和dirname(__FILE__)

我们可以用var_dump把这两个方法显示结果打印出来看一下

string 'E:\project\newapp' (length=17)

你会发现,两个打印结果一样。

但其实他们至少有两处区别:

  1. __DIR__只能用于PHP >= 5.3 版本,这也是为什么大多数你看到的代码中都是使用的dirname(__FILE__)这种写法
  2. __DIR__在编译时运算的,而dirname(__FILE__)要调用函数且在执行时运算

 

其他可以参考一下PHP手册,

__DIR__:文件所在的目录。如果用在被包括文件中,则返回被包括的文件所在的目录。它等价于

dirname(__FILE__)

。除非是根目录,否则目录中名不包括末尾的斜杠。

进程与线程及其区别

1.进程和线程

1.1 概述:

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.

相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。

在串行程序基础上引入线程和进程是为了提高程序的并发度,从而提高程序运行效率和响应时间。

1.2 区别:

进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

2) 线程的划分尺度小于进程,使得多线程程序的并发性高。

3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。

1.3 优缺点:

线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

2.多进程,多线程

2.1 概述:

进程就是一个程序运行的时候被CPU抽象出来的,一个程序运行后被抽象为一个进程,但是线程是从一个进程里面分割出来的,由于CPU处理进程的时候是采用时间片轮转的方式,所以要把一个大个进程给分割成多个线程,例如:网际快车中文件分成100部分 10个线程 文件就被分成了10份来同时下载 1-10 占一个线程 11-20占一个线程,依次类推,线程越多,文件就被分的越多,同时下载 当然速度也就越快

进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。显然,程序只是一组指令的有序集合,它本身没有任何运行的含义,只是一个静态实体。而进程则不同,它是程序在某个数据集上的执行,是一个动态实体。它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而被撤消,反映了一个程序在一定的数据集上运行的全部动态过程。进程是操作系统分配资源的单位。Windows下,进程又被细化为线程,也就是一个进程下有多个能独立运行的更小的单位。线程(Thread)是进程的一个实体,是CPU调度和分派的基本单位。线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

线程和进程的关系是:线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。

在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态,这便是多任务。现代的操作系统几乎都是多任务操作系统,能够同时管理多个进程的运行。 多任务带来的好处是明显的,比如你可以边听mp3边上网,与此同时甚至可以将下载的文档打印出来,而这些任务之间丝毫不会相互干扰。那么这里就涉及到并行的问题,俗话说,一心不能二用,这对计算机也一样,原则上一个CPU只能分配给一个进程,以便运行这个进程。我们通常使用的计算机中只有一个CPU,也就是说只有一颗心,要让它一心多用,同时运行多个进程,就必须使用并发技术。实现并发技术相当复杂,最容易理解的是“时间片轮转进程调度算法”,它的思想简单介绍如下:在操作系统的管理下,所有正在运行的进程轮流使用CPU,每个进程允许占用CPU的时间非常短(比如10毫秒),这样用户根本感觉不出来CPU是在轮流为多个进程服务,就好象所有的进程都在不间断地运行一样。但实际上在任何一个时间内有且仅有一个进程占有CPU。

如果一台计算机有多个CPU,情况就不同了,如果进程数小于CPU数,则不同的进程可以分配给不同的CPU来运行,这样,多个进程就是真正同时运行的,这便是并行。但如果进程数大于CPU数,则仍然需要使用并发技术。

Windows中,进行CPU分配是以线程为单位的,一个进程可能由多个线程组成,这时情况更加复杂,但简单地说,有如下关系:

总线程数<= CPU数量:并行运行

总线程数> CPU数量:并发运行

并行运行的效率显然高于并发运行,所以在多CPU的计算机中,多任务的效率比较高。但是,如果在多CPU计算机中只运行一个进程(线程),就不能发挥多CPU的优势。

 多任务操作系统(如Windows)的基本原理是:操作系统将CPU的时间片分配给多个线程,每个线程在操作系统指定的时间片内完成(注意,这里的多个线程是分属于不同进程的).操作系统不断的从一个线程的执行切换到另一个线程的执行,如此往复,宏观上看来,就好像是多个线程在一起执行.由于这多个线程分属于不同的进程,因此在我们看来,就好像是多个进程在同时执行,这样就实现了多任务.

 2.2 分类

根据进程与线程的设置,操作系统大致分为如下类型:

(1) 单进程、单线程,MS-DOS大致是这种操作系统;

(2) 多进程、单线程,多数UNIX(及类UNIX的LINUX)是这种操作系统;

(3) 多进程、多线程,Win32(Windows NT/2000/XP等)、Solaris 2.x和OS/2都是这种操作系统;

(4) 单进程、多线程,VxWorks是这种操作系统。

2.3 引入线程带来的主要好处:

(1) 在进程内创建、终止线程比创建、终止进程要快;

(2) 同一进程内的线程间切换比进程间的切换要快,尤其是用户级线程间的切换。

SNS网站Feed功能设计

在SNS的网站中,最核心的功能就是Feed功能,Feed就是一条twitter或一条好友动态。该功能面临的挑战是:每天产生成千上万条数据,数据推送的需要实时性等,做网站其实最大的难点就是对海量数据和高并发的处理。本人通过对Twitter和新浪微博架构的一些资料的学习,大致了解了如何实现一个Feed功能。一个Feed功能往往有多种实现方式,最常见的是这3种:推模式、拉模式、推拉结合模式。

推模式:

推就是把自己的发的feed推送到每个粉丝那里,就是一条feed在数据库中存储多份,做法就将feed表按userId分库,对应粉丝Id的库中插一条记录。这种做法的缺点就是数据量大,数据冗余太严重,如一个明星用户有1000万粉丝,那么他发一条feed,就要产生1000万条记录。Feed的推送需要异步队列,队列的好处就是降并发,推送是需要时间的,所以一个明星发了一条feed后,当最后那个粉丝看到这条feed可能已经是几分钟后的事情了。这种模式的优点是用户查看Feed就很容易了,根据userId查询就可以了。这种做法是牺牲大量储存空间来换取网站的查看性能。

拉模式:

拉就是用户要查看动态时就去每个关注者那边找,然后聚合并展现。Feed数据只储存一份,省了很多空间。这种模式在用户量较小的网站就很容易实现,展示只要一条SQL语句。但是当用户多了以后就会遇到问题,比如我关注了2000个用户,而这2000个用户都是活跃用户,每天都会产生很多feed。我查看 feed时就要去2000个关注者那里找最新的几条,合并和按时间排序,每次查看一条查询语句就快把数据库搞得累死,而且合并和排序这些feed计算量太大。拉模式在用户关注人数很多的网站不太适合。

推拉结合:

顾名思义就是两种模式的结合,将两者的优点结合。不活跃的用户(偶偶上线,关注人数又少的僵死用户等)推送的时候就不要推送给他们,省点资源,当他们上线查看时就直接拉,因为他们关注的人比较少,拉也不是很耗性能。

最后,不管采用什么方法实现,少不了异步队列,nosql等工具。比如发表feed的时候就往队列里一扔,前端马上返回,异步慢慢处理,而用户一点也察觉不出来。Nosql缓存,比如一些计数(关注数、粉丝数神马的)直接用redis、TT等储存,还有feed列表可以存储在memcached或redis 中,redis的list功能很适合这种情形,但是一些大网站还不敢这么做。

以上借鉴于新浪微博架构PPT。

JavaScript中的匿名函数及函数的闭包

1、匿名函数

函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途。匿名函数:就是没有函数名的函数。

1.1 函数的定义,首先简单介绍一下函数的定义,大致可分为三种方式

第一种:这也是最常规的一种

function double(x){
    return 2 * x;   
}

第二种:这种方法使用了Function构造函数,把参数列表和函数体都作为字符串,很不方便,不建议使用。

var double = new Function('x', 'return 2 * x;');

第三种:

var double = function(x) { return 2* x; }

注意“=”右边的函数就是一个匿名函数,创造完毕函数后,又将该函数赋给了变量square。

1.2 匿名函数的创建

第一种方式:就是上面所讲的定义square函数,这也是最常用的方式之一。

第二种方式:

(function(x, y){
    alert(x + y);  
})(2, 3);

这里创建了一个匿名函数(在第一个括号内),第二个括号用于调用该匿名函数,并传入参数。

2、闭包

闭包的英文单词是closure,这是JavaScript中非常重要的一部分知识,因为使用闭包可以大大减少我们的代码量,使我们的代码看上去更加清晰等等,总之功能十分强大。

闭包的含义:闭包说白了就是函数的嵌套,内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕(这点涉及JavaScript作用域链)。

示例一

function checkClosure(){
    var str = 'rain-man';
    setTimeout(
        function(){ alert(str); } //这是一个匿名函数
    , 2000);
}
checkClosure();

这个例子看上去十分的简单,仔细分析下它的执行过程还是有许多知识点的:checkClosure函数的执行是瞬间的(也许用时只是0.00001毫秒),在checkClosure的函数体内创建了一个变量str,在checkClosure执行完毕之后str并没有被释放,这是因为setTimeout内的匿名函数存在这对str的引用。待到2秒后函数体内的匿名函数被执行完毕,str才被释放。

示例二,优化代码

function forTimeout(x, y){
    alert(x + y);
}
function delay(x , y  , time){
    setTimeout('forTimeout(' +  x + ',' +  y + ')' , time);    
}
/**
 * 上面的delay函数十分难以阅读,也不容易编写,但如果使用闭包就可以让代码更加清晰
 * function delay(x , y , time){
 *     setTimeout(
 *         function(){
 *             forTimeout(x , y) 
 *         }          
 *     , time);   
 * }
 */

3、举例

匿名函数最大的用途是创建闭包(这是JavaScript语言的特性之一),并且还可以构建命名空间,以减少全局变量的使用。

示例三:

var oEvent = {};
(function(){ 
    var addEvent = function(){ /*代码的实现省略了*/ };
    function removeEvent(){}

    oEvent.addEvent = addEvent;
    oEvent.removeEvent = removeEvent;
})();

在这段代码中函数addEvent和removeEvent都是局部变量,但我们可以通过全局变量oEvent使用它,这就大大减少了全局变量的使用,增强了网页的安全性。 我们要想使用此段代码:oEvent.addEvent(document.getElementById('box') , 'click' , function(){});

示例四:

var rainman = (function(x , y){
    return x + y;
})(2 , 3);
/**
 * 也可以写成下面的形式,因为第一个括号只是帮助我们阅读,但是不推荐使用下面这种书写格式。
 * var rainman = function(x , y){
 *    return x + y;
 * }(2 , 3);
 */

在这里我们创建了一个变量rainman,并通过直接调用匿名函数初始化为5,这种小技巧有时十分实用。

示例五:

var outer = null;

(function(){
    var one = 1;
    function inner (){
        one += 1;
        alert(one);
    }
    outer = inner;
})();

outer();    //2
outer();    //3
outer();    //4

这段代码中的变量one是一个局部变量(因为它被定义在一个函数之内),因此外部是不可以访问的。但是这里我们创建了inner函数,inner函数是可以访问变量one的;又将全局变量outer引用了inner,所以三次调用outer会弹出递增的结果。

4、注意

4.1 闭包允许内层函数引用父函数中的变量,但是该变量是最终值

示例六:

/**
 * <body>
 * <ul>
 *     <li>one</li>
 *     <li>two</li>
 *     <li>three</li>
 *     <li>one</li>
 * </ul>
 */

var lists = document.getElementsByTagName('li');
for(var i = 0 , len = lists.length ; i < len ; i++){
    lists[ i ].onmouseover = function(){
        alert(i);    
    };
}

你会发现当鼠标移过每一个<li&rt;元素时,总是弹出4,而不是我们期待的元素下标。这是为什么呢?注意事项里已经讲了(最终值)。显然这种解释过于简单,当mouseover事件调用监听函数时,首先在匿名函数( function(){ alert(i); })内部查找是否定义了 i,结果是没有定义;因此它会向上查找,查找结果是已经定义了,并且i的值是4(循环后的i值);所以,最终每次弹出的都是4。

解决方法一:

var lists = document.getElementsByTagName('li');
for(var i = 0 , len = lists.length ; i < len ; i++){
    (function(index){
        lists[ index ].onmouseover = function(){
            alert(index);    
        };                    
    })(i);
}

解决方法二:

var lists = document.getElementsByTagName('li');
for(var i = 0, len = lists.length; i < len; i++){
    lists[ i ].$$index = i;    //通过在Dom元素上绑定$$index属性记录下标
    lists[ i ].onmouseover = function(){
        alert(this.$$index);    
    };
}

解决方法三:

function eventListener(list, index){
    list.onmouseover = function(){
        alert(index);
    };
}
var lists = document.getElementsByTagName('li');
for(var i = 0 , len = lists.length ; i < len ; i++){
    eventListener(lists[ i ] , i);
}