[转载]用Fiddler给别人的网站“优化”

mikel阅读(992)

[转载]用Fiddler给别人的网站“优化” – 梦想MVP – 博客园.

最近访问某知名网站的速度非常慢,有时候需要2分钟还没完全打开,页面展示了一半就卡住,然后等半天才继续显示 下面部分。这种情况已经有几个月了,不知道是他们服务器原因还是我所在网络的问题,但是基本上在其他网络访问速度也慢,如果是网站自身的问题,我自然没办 法解决,只能自己动手,先看看问题出在哪里,然后分析一下有没有可能绕过相关问题。于是用上了一个很好的HTTP分析工具,Fiddler Web Debugger

Fiddler的功能很强大,它不仅可以查看HTTP的通信信息,也可以进行分析,从而发现通信过程中的问题,为优化网站页面和提高性能提供依据。 另外,它提供了对url请求进行重定向处理(AutoResponder)的功能,用户可以在请求url时进行自定义处理,这也就为以制定方式访问页面提 供了可能性,这也是本文依靠的主要功能。

这个软件的具体使用方法可以参考《HTTP调试工具:Fiddler的使用方法介绍》本文只介绍如何用它来解决实际问题。

步骤一:获取与网站页面的通信信息

为了保证只监控到指定网页的通信信息,我只打开了一个IE,并且在fiddler设置只监控web browers(默认是All Processs)另外通过fiddler清除了IE缓存,从而能获取更加真实页面加载时间。然后在IE中输入相应网址(就不具体指明了)进行访问,这时 可以看到fiddler左侧出现许多访问这一网站的请求url,等待中……。直到IE状态栏显示加载完毕的提示“完成”,这时停止fiddler的通信捕 获,这样就完成了通信捕获。以下是局部抓图:

1

步骤二:分析性能瓶颈

接下来就是分析上面各个sessions(请求),找出哪些请求导致了加载页面慢,我一般按照如下步骤:

(1)找到不正确的请求

全选所有sessions,然后,选择右面的Statistics功能,这样就可以统计出这些sessions的总体信息,如下图(部分):

2

这里我关注的是从请求开始到返回请求的整个时间,这里消耗了2分56秒(Sequence (clock) time),显然,这是不可以接受的速度。然后我发现,在response codes中,统计的HTTP/502有12个,要知道,Http 5xx代表的是服务器错误,一般来说,这种错误会导致服务器反应很长时间才返回给客户端一个错误的代码信息,从而可能导致请求速度的急剧下降。有可能这个网站就是因为这些错误请求导致加载页面慢的。查了一下:

502 – 网关错误:如果试图运行的 CGI 脚本不返回有效的 HTTP 标头集,将出现此错误消息。若要解决此问题,必须调试 CGI 应用程序,以确定它为什么会将无效的 HTTP 信息传递给 IIS。

也就是说有12个url请求出现了这一错误。其实在左侧sessions中,也可以看出来,它以红色惊叹号开头,并且可以知道请求的url等信息。

3

(2)通过timeline功能查看所有消耗的时间

全选sessions后,在界面右侧的timeline功能,可以以图形方式统计请求各个文件耗费的时间(这里会以请求的文件为单位,将那些请求文件相同但是参数不同的url合并统计):

4

其中横坐标是时间,纵坐标是请求文件,可以分析一下究竟是页面还是图片等加载时间比较长,通过分析,确实是那12个错误请求导致的加载慢,其他的css、js和图片,消耗的时间可以忽略。

步骤三:解决性能瓶颈

性能问题本该是网站来解决的,我没那个能力去真正解决性能问题,这里只是通过别的方法,来绕过导致瓶颈的问题罢了。分析了一下这12个错误请求 url,归根结底就是一个adjs.php和gmjs.php,当然每个请求的参数有所不同。这其实在加载页面时候通过他们这些sessions前面图标 就可以看出是有问题的:

截图00

下箭头表示正在下载,然而,我观察了,在这个请求时,下载时间很长,卡住了很久,然后变成红色叹号图标,才继续请求别的url。

分析adjs.php和gmjs.php文件,从命名来看,应该是与页面中广告有关的,对于用户来说,不要这个请求也应该不影响浏览网页吧,所以这里就是要想办法在请求时候,如果是关于这两个文件的请求,想法子绕过去就好了。

这就用上了fiddler的AutoResponder功能。开篇介绍了,这个功能可以对请求进行拦截,并进行自定义处理。

首先选择“AutoResponder”选项卡,然后选择“Enable Automatic Responses”,选择“add”按钮来添加一个规则:

5

我这里选择如果请求包含“adjs/php ”(隐去前面的具体网址),则放弃(*drop)不去请求,当然这里的字符串是可以用正则表达式实现更为复杂的字符串匹配,另外,也可以选择其他匹配后的 处理方式,比如让本地模拟一个response,而不用真的到服务器请求了。

选择保存。再次按照步骤一,加载页面,cool,瞬间加载完毕,不影响浏览,广告少了一些,看来那两个导致加载慢的页面就是与广告相关的。

这种通过fiddler来过滤rul请求的功能,还可以实现更多功能,比如过滤掉对于音频、视频、图片等的请求,只获取浏览者需要的内容。

声明:我是此网站忠实的访问者。问题也许是我网络的问题也没准(比如说屏蔽着某些url的访问),我只是从浏览 者的角度,加快浏览速度而已。解铃还须系铃人,这个只是个临时方法,总不能每次都打开fiddler再访问网页吧?网站开发人员,才应该是使用 fiddler的最佳用户。

[转载]ASP.NET 编程:了解 IHttpHandler

mikel阅读(992)

[转载]了解 IHttpHandler – Luckdv – 博客园.

1 、概述

HttpHandlerProcessRequest

说明:HttpHandler是一个HTTP请求的真正处理中心。在HttpHandler容器中,ASP.NET Framework才调用HttpHandler的ProcessRequest成员方法来对这个HTTP请求进行真正的处理,真正地对客户端请求的服务 器页面做出编译和执行,并将处理过后的信息附加在HTTP请求信息流中再次返回到HttpModule中。

2、举例

以一个aspx页面为例,正是在HttpHandler这里,一个aspx页面才被系统处理解析,并将处理完成的结果继续经由HttpModule传递下 去,直至到达客户端。当然,对于aspx页面,ASP.NET Framework在默认情况下是交给System.Web.UI.PageHandlerFactory这个HttpHandlerFactory来处 理的。当一个HTTP请求到达这个HttpHandlerFactory时,HttpHandlerFactory会提供出一个HttpHandler容 器,交由这个HttpHandler容器来处理这个HTTP请求。 一个HTTP请求都是最终交给一个HttpHandler容器中的ProcessRequest方法来处理的。

3、HttpHandler

(1)实现HttpHandler,必须继承自IHttpHandler接口。下面是这个接口的定义:

using System;
namespace System.Web
{
    public interface IHttpHandler
    {
        //  其他Request是否可以使用IHttpHandler
        bool IsReusable { get; }
        //  处理HttpRequest
        void ProcessRequest(HttpContext context);
    }
}

(2)自定义HttpHandler

新建一个网站,default.aspx页面:default.aspx.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebApplication1
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Response.Write("<br/>来自Default.aspx页面<br/>");
        }
    }
}

新添一个类库MyHandler,添加一个类如下:

using System;
using System.Web;
using System.Web.SessionState;
namespace WebApplication1
{
    public class MyTestHandler : IHttpHandler, IRequiresSessionState
    {
        public bool IsReusable { get { return true; } }
        public void ProcessRequest(HttpContext context)
        {
            context.Response.Write("<h3><b>This is a HttpHandler Test</b></h3>");
            context.Session["Test"] = "<h3><span style=\"color:blue;\">在HttpHandler容器中调用Session</span></h3>";
            context.Response.Write(context.Session["Test"]);
        }
    }
}

(3)配置文件
在web.config文件的system.web节点下,添加:

      <add verb="*" path="*.aspx" type="WebApplication1.MyTestHandler, WebApplication1" />

(4)注意

<1>、.NET为ASP.NET提供了很多系统默认HttpHandler类,用来适应不同类型的HttpRequest。比如aspx,在machine.config中是这样定义的:
<add verb=”*” path=”*.aspx” type=”System.Web.UI.PageHandlerFactory”/>
这就说明遇到aspx的Request请求,asp.net会将其交给System.Web.UI.PageHandlerFactory的HttpHandler类来处理。
<2>、如果自己定义了新的HttpHandler,而且在web.config中指定,则系统只会使用这个新的HttpHandler,而不再使用原先默认的或者指定的.

4、HttpHandlerFactory

ASP.NET Framework实际不直接将相关的页面资源HTTP请求定位到一个其内部默认的IHttpHandler容器之上,而定位到了其内部默认的 IHttpHandler工厂上。IHttpHandler工厂的作用是对IHttpHandler容器进行调度和管理,这样做的优点是大大增强了系统的 负荷性,提升了效率。
(1)IHttpHandlerFactory接口
IHttpHandlerFactory接口包含两个方法:GetHandler方法返回实现IHttpHandler接口的类的实例,ReleaseHandler方法使工厂可以重用现有的处理程序实例。

using System;
using System.Web;
namespace System.Web.UI
{
    public class PageHandlerFactory : System.Web.IHttpHandlerFactory2, IHttpHandlerFactory
    {
        protected internal PageHandlerFactory();
        public virtual IHttpHandler GetHandler(HttpContext context, string requestType, string virtualPath, string path);
        public virtual void ReleaseHandler(IHttpHandler handler);
    }
}

(2) 实现一个简单的HttpHandler工厂
类库新添一个文件MyHandlerFactor.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WebApplication1
{
    public class MyHandlerFactory : IHttpHandlerFactory
    {
        public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
        {
            string fileName = url.Substring(url.LastIndexOf("/") + 1);
            string objName = fileName.Substring(0, fileName.IndexOf("."));
            string className = "WebApplication1." + objName;
            object objHandler = null;
            try
            {
                // 采用动态反射机制创建相应的IHttpHandler实现类。
                objHandler = Activator.CreateInstance(Type.GetType(className));
                context.Response.Write(className);
            }
            catch (Exception e)
            {
                throw new HttpException("工厂不能为类型" + objName + "创建实例。", e);
            }
            return (IHttpHandler)objHandler;
        }
        public void ReleaseHandler(IHttpHandler handler)
        {
        }
    }
    public class Handler1 : IHttpHandler
    {
        public bool IsReusable { get { return true; } }
        public void ProcessRequest(HttpContext context)
        {
            context.Response.Write("<br/>这是来自于MyHandlerFactory里的处理.<br/>");
            context.Response.Write("<h3>来自Handler1的信息.</h3>");
        }
    }
    public class Handler2 : IHttpHandler
    {
        public bool IsReusable { get { return true; } }
        public void ProcessRequest(HttpContext context)
        {
            context.Response.Write("<br/>这是来自于MyHandlerFactory里的处理.<br/>");
            context.Response.Write("<h3>来自Handler2的信息.</h3>");
        }
    }
}
   <add verb="*" path="Handler1.aspx" type="WebApplication1.MyHandlerFactory,WebApplication1"/>
   <add verb="*" path="Handler2.aspx" type="WebApplication1.MyHandlerFactory,WebApplication1"/>

到这里,针对Handler1.aspx和Handler2.aspx两个页面的http请求我们就通过HttpHandler工厂处理好了。

5、HttpHandler和HttpModule的区别

主要有两点:
(1)先后次序.先IHttpModule,后IHttpHandler,IHttpHandler处理结束后再交给IHttpModule;
(2)对请求的处理上:
IHttpModule是属于大小通吃类型,无论客户端请求的是什么文件,都会调用到它;例如aspx,html,rar的请求;
IHttpHandler则属于挑食类型,只有asp.net注册过的文件类型(例如aspx,ascx,asmx等等)才会轮到调用它。

download2

[转载]Android:Text中电话号码、网址自动链接

mikel阅读(907)

[转载]android—Text中电话号码、网址自动链接 – 莴笋炒肉 – 博客园.

假若TextView文本中有电话号码或者网址,我想通过点击电话号码或者网址就能实现打电话或者打开网页,Android中已经为我们提供这样的属性和方法进行设置,大体可以分为三种:

1、设置TextView的autoLink属性:他有几个值all、web、phone、email等。当文中有这几种类型的文本值时,点击它将进入网页、打电话或者email的activity,这是最简单的方法

2、在文本值直接添加链接

(1)例如在string.xml文件中:<string><a href=http://www.google.com>http://www.google.com</a> <a href=”tel:18600000001″>tel</a> </string>,同时设置TextView属性 setMovementMethod(LinkMovementMethod.getInstance());

(2)在代码中使用Hteml.fromHtml构建文本

代码

tv2.setText( Html.fromHtml("the google url: " + "<a href=\"http://www.google.com\">http://www.google.com</a><br/>" + "the telephone: " + "<a href=\"tel:18603045201\">18603045201</a>)" )); tv2.setMovementMethod(LinkMovementMethod.getInstance());

3、使用SpanableString指定某段字串为链接文本

代码

TextView tv3=(TextView)findViewById(R.id.tv3); SpannableString ss= new SpannableString("the google url: http://www.google.com 18600000001"); ss.setSpan(new URLSpan("http://www.google.com"), 16, 37, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ss.setSpan(new URLSpan("tel:18603045201"), 38, 49, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); tv3.setText(ss); tv3.setMovementMethod(LinkMovementMethod.getInstance());

[转载]巧用Webbrowser实现网络数据采集

mikel阅读(1111)

[转载]新手入门:巧用Webbrowser实现网络数据采集 – 学院派的驴 – 博客园.

作者:finallyliuyu 出处:博客园(博文转载请标明作者和出处)

编者按:本系列文章给出的网络数据采集方法、思路、和框架并无工业化应用价值,但足以满足各高校实验室在实验阶段爬去语料库,获取网络资源的需求。

欢迎老鸟指点,但是此篇博文的定位是“写个菜鸟,新手的”所以禁止无厘头的疯狗式乱骂。

在上一篇文章:《巧用C# webbrowser实现动态网页爬虫机器人》 中,给出了一个综合利用webbrowser控件,MSHTML DOM,正则表达式,以及线程阻塞技术爬取动态网页链接的框架和方法。(注:所谓动态网页是指:目标网页的URL不能从当前网页中直接获得,而是要依赖 JavaScript重新定向和生成。这种动态网页,我们有的时候可以通过查看页面<script>元素的内容找到蛛丝马迹,当更多时候,生 成目标网页的URL的JS函数的源码不再当前网页,这时候就显得无能为力了)。《巧用C# webbrowser实现动态网页爬虫机器人一文中的框架方法也同样适用于静态网页,可以说这个框架方法在某种程度上具有一定的通用性。此篇博文是对上一篇博文的一个补充,实现的功能是:从一级索引页获取到二级索引页首页的链接,在二级索引页首页获取当前页面中指向内容页面的URL链接,并且进行翻页获取内容页源码,同时完成二级索引页首页到次页的翻页,并以此类推。(注意:索引页,内容页的定义请见:《巧用C# webbrowser实现动态网页爬虫机器人》)

网络数据采集是网络挖掘的一个重要步骤。属于预处理工作范畴。一般分为两步,网络信息采集(即下载网页源码)和网络信息解析(即对网页源码进行解析,提取出BOI (block of interest))。本文是对《巧用C# webbrowser实现动态网页爬虫机器人》的补充,完成网络信息采集功能。

下面给出关键模块代码:

文章实体

public class ArticlePage
{
public string title;
public string url;
public string rawtext;
public ArticlePage()
{
title
= string.Empty;
url
= string.Empty;
rawtext
= string.Empty;
}

}

信号变量定义

public bool mysignal1;//btnworkflow按钮是否被点击
public bool mysignal2;
public bool mysignal3;
public bool loading;//工作流按钮与webbrowser进行交互的通信按钮
public bool subloading;
public bool subloadingPer;
信号变量初始化

public Form1()
{
InitializeComponent();
mysignal1
= false;
mysignal2
= false;
mysignal3
= false;
loading
= true;
subloading
= true;
subloadingPer
= true;
}
webbrowserCompleted更新信号

private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (webBrowser1.ReadyState ==WebBrowserReadyState.Complete)
{
if (mysignal1)
{
if (!mysignal2&&!mysignal3)
{
loading
= false;
}
else
{
if (mysignal2&&!mysignal3)
{
subloading
= false;
}

if (mysignal3)
{
subloadingPer
= false;
}

}

}

}

}

实现自动爬虫的工作流

private void btnworkflow_Click(object sender, EventArgs e)
{
mysignal1
= true;
string dirCurPageUrl = string.Empty;//用于恢复目录页当前页的网页视图
List<ArticlePage> arListCurrentPage;
foreach (string s in issuesMap)
{

loading = true;
string tmpurl = s;
webBrowser1.Navigate(tmpurl);

while (loading == true)
{
Application.DoEvents();
}

arListCurrentPage = GetArticlePageInfoFromCurrentDirpage();
if (arListCurrentPage != null)
{
dirCurPageUrl
= webBrowser1.Url.ToString();
mysignal3
= true;
foreach ( ArticlePage ap in arListCurrentPage)
{
webBrowser1.Navigate(ap.url);
subloadingPer
= true;
while(subloadingPer)
{
Application.DoEvents();
}
ap.rawtext
= webBrowser1.DocumentText;
}
InsertTitleUrlToDataBase(arListCurrentPage);
mysignal3
= false;
webBrowser1.Navigate(dirCurPageUrl);
loading
= true;
while (loading == true)
{
Application.DoEvents();
}
}

mysignal2 = true;
while (AnchorNextPage())
{
subloading
= true;
while(subloading)
{
Application.DoEvents();

}
arListCurrentPage = GetArticlePageInfoFromCurrentDirpage();
if (arListCurrentPage != null)
{
dirCurPageUrl
= webBrowser1.Url.ToString();
mysignal3
= true;
foreach (ArticlePage ap in arListCurrentPage)
{
webBrowser1.Navigate(ap.url);
subloadingPer
= true;
while (subloadingPer)
{
Application.DoEvents();
}
ap.rawtext
= webBrowser1.DocumentText;
}
InsertTitleUrlToDataBase(arListCurrentPage);
mysignal3
= false;
webBrowser1.Navigate(dirCurPageUrl);
subloading
= true;
while (subloading)
{
Application.DoEvents();

}

}

}
mysignal2 = false;

//获得当前页面的下一页链接

}

}

数据库操作

private void InsertTitleUrlToDataBase(List<ArticlePage> arlist)
{
DataBaseManipulation dm
= new DataBaseManipulation();
string conStr = server=(local);database=xxxxx;uid=sa;pwd=xxx;
dm.ConstructConnection(conStr);
foreach (ArticlePage article in arlist)
{
dm.InsertToDataBase(article,
xxx);
}

}

数据库操作2

public void InsertToDataBase(ArticlePage article,string table )
{
//插入字符串
string SQLcommand=string.Format(insert  into {0}(ArticlePageUrl,ArticlePageTitle,ArticlePageSource)values(@ArticlePageUrl,@ArticlePageTitle,@ArticlePageSource),table);

//数据库参数构造与赋初值
SQLParameter ArticlePageTitle = new SqlParameter(@ArticlePageTitle, SqlDbType.VarChar, 400);
ArticlePageTitle.Value
= article.title;
SqlParameter ArticlePageUrl
= new SqlParameter(@ArticlePageUrl, SqlDbType.VarChar, 400);
ArticlePageUrl.Value
= article.url;
SqlParameter ArticlePageSource
= new SqlParameter(@ArticlePageSource, SqlDbType.Text);
ArticlePageSource.Value
= article.rawtext;
SqlCommand cmd
= new SqlCommand(sqlcommand, connection);
cmd.Parameters.Add(ArticlePageTitle);
cmd.Parameters.Add(ArticlePageUrl);
cmd.Parameters.Add(ArticlePageSource);

//打开数据库连接
OpenConnection();

try
{
//执行cmd操作
cmd.ExecuteNonQuery();
}
catch (System.Exception e)
{
//输出错误到记事本中
StreamWriter sw = new StreamWriter(D:\\myerror.txt, true, Encoding.Default);
sw.Write(e.Message);
sw.Close();
//一旦发生错误程序就停止运行,等待用户发现
Console.Read();

}

//关闭数据库连接
CloseConnection();

}

}
}

本地数据库(保存爬取的信息)视图:

小结与发散:笔者于本科毕设做过新闻类网页正文提取的课题,请见《新闻类网页正文提取系列》,并且从各大新闻门户网站的不同版面提取了四万余条新闻作为语料库,该语料库已经提供给网友下载,语料库说明以及下载地址见:《献给热衷于自然语言处理的业余爱好者的语料库》。 但是毕设当时,我对于动态网页提取方法并没有一个清晰的认识,所以可以提取的新闻版面上限制很大(必须是静态新闻网页才能提取)。有兴趣,并且对语料库有需求的网友,可以参考我的此篇博客,以及上一篇博客《巧用C# webbrowser实现动态网页爬虫机器人》,还有正文提取系列的一些博文,自己动手配置一个自动获取新闻语料库的小型爬虫。

[转载]介绍下Nupack如何在asp.net mvc 2中使用

mikel阅读(1122)

[转载]介绍下Nupack如何在asp.net mvc 2中使用 – 爱因斯坦的小脑 – 博客园.

Nupack是一个打包好的.net工具集,Nupack团队开发它是为了让开发人员能够很容易得救把第三方工具集成到你的项目中。

image

Package Management并不是一个新概念,在之前Unixm,Ruby等里面都有过这个概念。

关于Nupack的介绍之前在ScottGu的博客http://weblogs.ASP.NET/scottgu/archive/2010/10/06/announcing-nupack-asp-net-mvc-3-beta-and-webmatrix-beta-2.aspx

image

但是对于我们英语不大好的开发人员来说,看这个还是有点难度的。我会在这里从安装到使用,详细的介绍下。

很喜欢VS2010的Extension Manager Tool,直接使用它就可以把NuPack给安装好,当然了我们是说你用的是ASP.NET MVC 2,如果是是安装了ASP.NET MVC3 Preview那么你不需要再去安装一次Nupack,它已经自动安装好了。

如果是ASP.NET MVC2,别急,你可以通过Extension manager Tool来安装。

打开EMT,选择Online Gallery,在右上角的搜索框搜Nupack,:

image

因为我这里已经安装好了,你可以点击download来下载好,然后安装好。重启vs2010。

下面我给大家说明下如何使用Nupack。先创建一个ASP.NET MVC 2的项目:

image

1.如何打开Package Manager Console?

PMC默认不显示在VS中,你需要去View->Other windows->Package Manager Console这里选中它,它就会显示在VS下方。

image

2、如何使用NuPack命令

第一个命令是List-Package:,你直接在Package Manager Console下输入List-Package,就会看到NuPack中目前的所有第三方工具了,如下图:

image

如果你需要把某个第三方工具添加到你的项目中,只需要输入Add-Package XXX ,例如我们需要把’elmah’添加到项目中,只需在PMC中输入如下:

image

你如果不知道elach是干嘛的请去 google code上看一下这个open source项目的介绍。它主要是用来记录你的网站报错信息,可以把错误信息通过email,twitter等发送给你,也可以保存在数据库中。上面的操 作已经把这个第三方工具添加到你的项目中,同是还修改了web.config文件,也就是说连配置都给你自动改了。很不错。

image

现在试试是否elmah能够正常使用,我们先来随意输入一个url让系统报错我输入一个shit/shit。

image

然后访问elmah.axd看看是否错误日志已经记录下来了。

image

不错,已经记录下来了。。。。。

接下来我们试试添加NHibernate.linq看看还有什么神奇的功能。

image

哇,它会自己把需要用到的相关工具自己下载下来。

如果想移除NHibernate.linq你只要在控制台输入如下命令:

PM> Remove-Package NHibernate.Linq –RemoveDependencies
如果你不想使用Dos命令,你可以通过添加Package来直接搞定。
image
还有其他很多的功能,我希望后面我可以抽个时间个大家介绍,你们如果对这个感兴趣也可以自己研究下。不写啦今天。呵呵。
Cheers
Nick
要是有人需要这个项目的代码可以留言。下面可以输入邮件。。。其实代码也没什么的。

[转载]Excel超链接的应用-在Excel里输入身份证号,自动生成查找身份证信息的链接

mikel阅读(1257)

[转载]Excel超链接的应用-在Excel里输入身份证号,自动生成查找身份证信息的链接 – 耕者 – 博客园.

今天公司一位同事告诉我,她想把在Excel里输入的身份证号码设置超链接,直接点击就可以链接到网页里显示查找的信息,问题很突然,正常情况下我们都知道设置超链接时直接是把固定链接地址设置到超链接即可,而这里显然是要传递参数才行,就以http://www.ip138.com/ 的身份证查询链接为例:http://qq.ip138.com/idsearch/index.asp?action=idcard&userid=123456789012345678&B1=%B2%E9+%D1%AF 其中123456789012345678就是要传递的参数(这里表示身份证号码),如何完成这一自动填写参数过程呢,首先我想到了两个公式:超链接HYPERLINK和文本截取SUBSTITUTE

公式语法:

HYPERLINK(link_location, friendly_name)
link_localtion:链接地址

friendly_name:为单元格内要显示的信息

SUBSTITUTE(text,old_text,new_text,instance_num)

Text:为需要替换其中字符的文本,或对含有文本的单元格的引用

Old_text : 为需要替换的旧文本

New_text :用于替换 old_text 的文本

Instance_num :为一数值,用来指定以 new_text 替换第几次出现的 old_text。如果指定了 instance_num,则只有满足要求的 old_text 被替换;否则  将用 new_text 替换 Text 中出现的所有 old_text

我的示例:

假如我在A1单元格里输入身份证号码”123456789012345678″(注意要用文本格式),将D1单元格最为自动生成的超链接:公式如

” =HYPERLINK(SUBSTITUTE(“http://qq.ip138.com/idsearch/index.asp?action=idcard&userid=000000000000000000&B1=%B2%E9+D1%AF”,”000000000000000000″,A1,1),A1) ” ,再D1单元格的数据将自动显示为123456789012345678的超链接,单击即可进入网页查询身份信息

[转载]PDC 2010:C#与Visual Basic的未来(中)

mikel阅读(1124)

[转载]PDC 2010:C#与Visual Basic的未来(中) – 老赵点滴 – 追求编程之美.

PDC 2010:C#与Visual Basic的未来(中)

2010-10-31 21:49 by 老赵, 382 visits

前几天在PDC 2010会议上Anders Hejlsberg发表了一场名为“The Future of C# and Visual Basic”的演说,谈论了未来C#和VB中最为重要的两个特性:“异步(Async)”及“编译器即服务(Compiler as a Service)”。我现在对这场演讲进行总结,但不会像上次《编程语言的发展趋势及未来方向》那样逐句翻译,而是以Anders的角度使用一种简捷合适的方式表述其完整内容。上一篇Anders讲述了async和await的使用方式,而这篇则是对这两个关键字的实现及效果作更进一步的解释。

异步方法的目标,是为了让代码与同步方法保持一致。微软要让代码充斥着回调函数,混乱不堪,它们完全不是逻辑上你想做的事情。可能您的代码中包含着一个核心模型,你也已经实现了,只是您现在想把它的执行过程变得异步化。您自己就可以享受到这一点。

与我们之前做的一些扩展一样,工作分为语言和框架两部分。语言方面的异步工作是基于Task<T>的,我们会围绕着 Task<T>扩展框架,将它作为异步模型的核心。事实上,从Begin/End,或是基于事件的异步模型进行扩展往往只需要一两行封装的代 码,于是您也可以得到自己的Task<T>模型。

而在语言方面,我们添加了两个新的关键字。一个是async关键字,用于把方法标记为异步。还有一个是await方法,用于等待异步工作完成,或者说是把控制权交换给调用方继续执行其他工作。这两个功能在C#和VB种均有体现。

那么什么是Task<T>呢?它表现的是一个“后续会继续进行的操作”,这可以是许多东西,Task<T>并不做任何限制,例如是一个异步I/O,后台工作线程等等,甚至可以是UI上的一个按钮,在用户点击之后任务就结束了。

Task<T>的优势在于,它使用一个对象封装了整个概念,您可以查询其结果或是状态,或是这个任务所引发的异常。您可以用它来构造一个可组合的异步模型,这正式我们目前的异步编程模型所不足的地方。

此外,它还提供了一个可组合的回调模型,您可以对一个任务指定说,在它结束之后执行另外一段代码,然后还可以对这个新的任务继续进行设定。这便构造 出一个完整的逻辑流,框架会自行帮你完成这些工作。事实上await操作符便会自动把您的逻辑改写成这样的代码,它将您从Lambda表达式及回调函数中 的逻辑里解放了出来,一切都交给编译器去做了。您可能会有些疑惑,不过其实这些都是编译器所擅长的事情。

由于我们统一了异步模型,我们就可以在此之上构建组合工具。例如WhenAll,它接受一系列的Task对象,并在全部结束之后返回所有结果。还有WhenAny,则等待第一个完成的任务,返回其结果。我们还有Delay,可以等待一段时间,但不占用任何资源。

沿着这个过程走一遍可能就会清晰一些。这里有个例子,一个异步方法调用另一个异步方法。我们假设这是在UI线程上执行的,消息会一个一个发送至UI线程上。

好,有人调用了DoWorkAsync,于是出现了一些任务。

DoWorkAsync的第一件事,是调用了ProcessFeedAsync。

ProcessFeedAsync方法是一个异步方法,所以它做的第一件事是构造一个表示任务的Task对象。

然后它调用了DownloadFeedAsync,这会创建另一个Task对象。然后,我们遇上了await操作符,这意味着ProcessFeedAsync后面的部分,将作为DownloadFeedAsync完成后的回调函数/continuation里的工作。

于是任务返回至DoWorkAsync,我们得到了t1这个对象。

同样的过程会再次出现,是为t2。

然后便调用了Task.WhenAll,这会创建一个新任务,表示前两个任务全部完成。于是这里的await操作符表示接下去的代码会在前两个任务完成后再继续下去。此时控制权便还给了DoWorkAsync的调用者,不会对线程造成负担。

在未来某一时刻t1和t2会执行完,我们假设t2先结束。此时它会说:我完成了,执行回调函数/continuation吧。

于是它会和发起线程的SynchronizationContext交互,给UI线程发一个消息,让后续任务在UI线程上继续执行──您的代码不用 关注这些。现在代码运行至SaveDocAsync上了,这是另外一个异步任务。await让代码在这里返回,线程又可以执行目前还未结束的任务了。

于是SaveDocAsync任务完成了,UI线程又获得了一个消息执行后续工作。

此时任务便到达了ProcessFeedAsync的末尾,于是t2任务结束了。

继续等待,上面的过程会再次出现,最终t1也结束了。

当t1和t2完成以后,最后DoWorkAsync任务也终于结束了。可以看到,我们逻辑流程,无论是循环还是异常捕获都是同步的,但是其中的执行过程完全是异步的。

但是这又是如何实现的?我不会在这里说太细,这又是个完整的话题了。这里有一个例子,是一个异步方法,它会调用并await另一个异步方法。

而编译器则最终则生成类似于这样的代码。我只会提几点,首先,这是个状态机,编译器构造的其实就是个状态机,例如迭代器就是个状态机,事实上这里编译器的工作和yield之余迭代器的重写本质上没有太大区别。

其次就是关于任务的执行和等待,假如在等待时任务已经完成了,那么其实您是在同步地执行后续代码。我们没有必要交还控制,反正已经完成了,我们不妨 就直接进行下去了。await有自己的模式,会决定这一任务是同步还是异步地执行。对于同步执行的任务,一切就继续执行下去了,直到某个需要异步执行的地 方,便把控制权交还给调用方。

那么我们再来看一下异步之于Web服务的意义。这里有个ASP.NET页面,它会向数据库里获取许多RSS地址,然后下载到本地并解析:

private void ProcessData()
{
    // ...

    var urls = new List<string>();
    using (var conn = new SqlConnection(connectionString))
    {
        conn.Open();
        var cmd = new SqlCommand("GetUserFeeds", conn);
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.AddWithValue("@UserID", user);
        using (var reader = cmd.ExecuteReader())
        {
            while (reader.Read()) urls.Add(reader["FeedURL"].ToString());
        }
    }

    var feeds = (from url in urls select CreateWebClient().DownloadString(url)).ToArray();

    // ...
}

这里用到了DownloadString这个同步下载数据的方法。执行下来大约要花费1秒多的时间。这里我不再演示令人痛苦的异步写法了,你必须在 Page_Load和Page_PreRender各写一些逻辑,注册一些异步工作,或者就要启用一些后台线程,但这又会影响后台的线程池,对系统的表现 会带来影响。

现在我来演示一些简单的异步化工作:

private async void ProcessData()
{
    // ...

    var urls = new List<string>();
    using (var conn = new SqlConnection(connectionString))
    {
        conn.Open();
        var cmd = new SqlCommand("GetUserFeeds", conn);
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.AddWithValue("@UserID", user);
        using (var reader = await cmd.ExecuteReaderAsync())
        {
            while (reader.Read()) urls.Add(reader["FeedURL"].ToString());
        }
    }

    var feeds = await TaskEx.WhenAll(
        from url in urls select CreateWebClient().DownloadStringTaskAsync(url));

    // ...
}

我们将DownloadString修改为DownloadStringTaskAsync,这样LINQ返回的就是一系列表示下载任务的Task 对象,然后使用await及WhenAll等待它们全部完成。数据库查询也可以如此。这就是所有我们要做的事情。如今页面的执行效率有了很明显的提高。使 用这个做法,我们可以很轻松地提高Web系统的伸缩能力。如今我们需要调用很多互相独立的服务的情况越来越多了,异步方法对此有很大帮助。

如今的异步场景有许多种,例如在后台执行一个计算任务,这是基于CPU的异步,还有基于网络或I/O的异步任务。这些都能用Task来表示出来,因 为Task表示的就是未来会完成的异步任务。此外,有了async和.NET框架,我们则出现了另外一种任务,既基于某些任务组合而成的异步任务。这也就 是async方法所体现出的异步任务,它可以让你使用传统的语句来构造异步执行过程。

例如有这么一个场景:获取链接,根据链接下载Youtube视频,根据下载到的视频创建mashup并组合起来。在执行这些工作的时候,我们也希望UI可以响应用户操作。

而要完成这些工作,代码可能只需要这么简单,完全就像同步代码一样。而这里也体现了多种异步任务:ScrapeYoutubeAsync是网络密集 型任务,然后同时下载两个视频并等待它们结束。然后MashupVideosAsync是CPU密集型任务,然后最后则是I/O密集型的的 SaveAsync操作。对于异常处理来说,我们可以简捷地使用一个try…catch,就像传统编程那样。

总结一下,一个异步方法可以让代码和同步实现一样简单,并统一了计算、网络及I/O的异步化。这可以用来创建高度伸缩的服务器程序,自然还有响应度高的UI程序。

在演讲的末尾,我会给出Visual Studio Async CTP的下载链接,我很乐于得到大家的反馈。

相关文章

[转载]PDC 2010:C#与Visual Basic的未来(上)

mikel阅读(869)

[转载]PDC 2010:C#与Visual Basic的未来(上) – 老赵点滴 – 追求编程之美.

PDC 不愧为微软最高级的技术人员专业会议,看得我直呼过瘾。前几天在PDC 2010会议上Anders Hejlsberg发表了一场名为“The Future of C# and Visual Basic”的演说,谈论了未来C#和VB中最为重要的两个特性:“异步(Async)”及“编译器即服务(Compiler as a Service)”。我现在对这场演讲进行总结,但不会像上次《编程语言的发展趋势及未来方向》那样逐句翻译,而是以Anders的角度使用一种简捷合适的方式表述其完整内容。

在 2000年的PDC上,我们给大家带来了一个全新的平台“.NET”,以及一个语言“C#”。.NET与C#每次发布时都有一个“主题”,一开始是“托管 代码”,接着是“泛型”,然后是“LINQ”,直到最近的“动态性”,这就是C#和VB的演变过程。这两种语言面向的用户比较相近,微软也承诺会同时发展 两种语言。因此这个演讲虽然以C#作为主题,但其实也会在VB中得以体现。

作 为语言的设计者,要设法将工业界所重视的内容,使用语言表现出来,因此也有了这样的分类。“声明式”代表了一种编程的趋势,尽可能表现出“做什么”而不是 “怎么做”,于是有了函数式编程与DSL等等。然后,目前研究的热门之一则是动态语言,如Python,Ruby,JavaScript等等,以及它们是 如何影响静态语言的。还有便是“并发”,这里所指的广义的“并发”,包括单机上多核以及云或是数据中心上分布式系统等等,也就是各种“同时处理”的方式。

我 们可以清楚地看到,C# 3.0和VB 9中的函数式编程,LINQ等特性体现了“声明式”,而C# 4.0和VB 10则出现了动态性,但都没有太多关于“并发”的成分在里面──它都体现在框架中了,例如.NET 4包含了任务并行库(Task Parallel Library),但对于语言来说,除了lock似乎就没有什么这方面的支持了。

如 今对“并发”的需求已经是毋庸质疑的,很少有一个应用程序或是服务不需要连接外部系统。这种与外部系统,例如互联网进行交互的行为则增加了应用程序的延 迟,这可能导致UI在和外部服务交互时长时间失去响应。而对于一个数据中心的服务,您可能就会发现CPU的利用率不高,因为系统都在等待其他服务的回复 了。

为了解决这个问题,我们往往会使用“异步”的编程方式,它逐渐已经成为“高响应度”,“高伸缩性”的代名词了。此外还有一些API只 提供了异步的版本,例如在JavaScript中发起HTTP请求,或是Silverlight的网络交互方面。这种情况以后只会越来越普遍。

于是下一版本的C#和VB就会在这里有所行动,目前会展示一下我们的早期工作,希望可以得到一些反馈。

说 到异步化,您可以简单认为“一起运行”。一个同步方法,好比DownloadString,应用程序会执行这个方法,并等待结果返回,但是你不能把工作的 执行过程与结果的送达区分开来。而对于异步编程来说,DownloadStringAsync在调用之后便会立即返回,过了一段时间,结果就会传递过来, 于是执行过程和结果的送达便完全是可分离的了。而对于如今典型的异步模型来说,结果通过一个回调函数传递过来。

异步化可以的得到高度的响应能力,因为在等待任务的结果时我们可以做其他一些事情。而对于服务器来说,异步可以带来很好的伸缩性,因为线程得到释放了,而不需要等待请求返回结果。

通 过图示可以更清楚地了解这点。例如有段代码叫做DownloadData,调用以后可以得到一些数据。在执行时,线程会有长时间的终止,它被阻塞了,要等 到结果返回之后才能继续处理数据。与此相对的是其异步的版本,我们调用DownloadDataAsync方法之后,它立即将控制权交还给我们,过了一段 时间,它会把结果传递给回调函数,让我们继续处理下去。但是在DownloadData和ProcessData之间,我们可以处理其他一些工作。如果这 是UI线程,那么就可以用于响应其他用户操作。如果这是个服务器线程,那么在等待结果时这个线程可以用来处理其他请求。

那么,如果我们要执行多个请求,例如要调用两遍,对于同步的版本就会获得双倍的阻塞,即便两个请求是完全独立的。而在异步的情况下,我们可以快速地发出两个请求,这样便形成的并发,即便这里并没有使用额外的线程。于是便可以更快地得到结果,也能保证响应能力。

有 人可能会说,我们可以利用后台线程来得到响应。没错,不过就引入了多线程模型,于是就要处理同步等线程安全问题。而且,在开发带有UI的应用程序时,我们 不能在后台线程里操作UI,这样又出现了其他的复杂情况。而在服务器应用中,我们又不希望创建更多的线程,因为这会给线程池带来压力,线程之间会有竞争, 就会降低请求的处理能力。

以上便是对异步编程的概述,您可能会问,既然异步有那么多好处,那么为什么不把所有的应用程序都写作异步的呢?那么现在我们就来看一下异步编程大概是什么样子的。

这里有个简单的应用程序,输入年份,可以下载到那一年的电影。现在这个程序是同步的写法。在搜索的时候UI会失去响应,这样的结果显然无法令人接受,我们要做的更好。我们可以将其改写为异步的形式。

同步的写法是这样的:

private void searchButton_Click(object sender, RoutedEventArgs e)
{
    LoadMovies(Int32.Parse(textBox.Text));
}

void LoadMovies(int year)
{
    resultsPanel.Children.Clear();
    statusText.Text = "";
    var pageSize = 10;
    var imageCount = 0;

    while (true)
    {
        var movies = QueryMovies(year, imageCount, pageSize);
        if (movies.Length == 0) break;
        DisplayMovies(movies);
        imageCount += movies.Length;
    }

    statusText.Text = String.Format("{0} Titles", imageCount);
}

Movie[] QueryMovies(int year, int first, int count)
{
    var client = new WebClient();
    var url = String.Format(query, year, first, count);
    var data = client.DownloadString(new Uri(url));

    var movies =
        from entry in XDocument.Parse(data).Desendanies(xs + "entry")
        let properties = entry.Element(xm + "properties")
        select new Movie
        {
            /* ... */
        };

    return movies.ToArray();
}

在点击按钮以后会调用LoadMovies方法,它会在一个循环中不断使用QueryMovies方法进行查询,在QueryMovies方法中我们使用WebClient下载一个XML,解析,构造Movie对象并返回,最终呈现在界面上。

下 载时我们使用DownloadString方法,这是个同步方法,我们要把它修改成异步的方式。事实上还真有个异步的方法,叫做 DownloadStringAsync,不过这就需要我们修改代码,例如要把QueryMovies中的大部分放入 DownloadStringCompleted事件的处理函数中。同时,异步编程的痛苦慢慢体现出现了,我们无法返回数据,而必须传递到某个地方,于是 QueryMovies方法则要返回void,并接受一个回调函数。

void QueryMovies(int year, int first, int count, Action<Movie[]> action)
{
    var client = new WebClient();
    var url = String.Format(query, year, first, count);

    client.DownloadStringCompleted += (sender, e) =>
    {
        var data = e.Result;
        var movies =
            from entry in XDocument.Parse(data).Descendants(xs + "entry")
            let properties = entry.Element(xm + "properties")
            select new Movie
            {
                /* ... */
            };

        action(movies.ToArray());
    };

    client.DownloadStringAsync(new Uri(url));
}

然后我们还需要处理QueryMovies的调用者,这里实在麻烦到家了,因为我们使用了一个while循环来查询电影,那么我们又该如何反复调用一个异步方法?

void LoadMovies(int year)
{
    resultsPanel.Children.Clear();
    statusText.Text = "";
    var pageSize = 10;
    var imageCount = 0;

    Action<Movie[]> action = null;
    action = movies =>
    {
        if (movie.Length > 0)
        {
            DisplayMovie(movies);
            imageCount += movies.Length;
            QueryMovies(year, imageCount, pageSize, action);
        }
        else
        {
            statusText.Text = String.Format("{0} Titles", imageCount);
        }
    };

    QueryMovies(year, imageCount, pageSize, action);
}

你一定已经发现了,现在的代码已经很难让人保持愉快了。不过它的确是异步的了,运行时界面响应良好。效果是有了,不过这代码变得乱七 八糟。想象一下,如果要加上异常处理该怎么做?我们可能要提供两个回调函数,一个处理正常情况,一个处理错误,还到处需要有try…catch,很快 麻烦就会接踵而来了。如果不想面对这些麻烦,你可能就要去启用后台线程,这样又有了线程方面的问题。

显然我们可以做的更好。首先让我们回到原来的同步代码,然后再用上我们为异步编程设计的新特性。

如 果要把QueryMovies变为异步,则先把它的返回值改为Task<Movie[]>,你如果了解.NET 4则一定已经知道这个类型是任务并行库的一部分。事实上Task类型只是表示一个“开始计算并在未来返回结果”的任务,因此Task<T>表 示一个会在将来返回T类型的计算任务,在科学计算领域这通常被称为Future或是Promise。现在方法的返回值是 Task<Movie[]>,而最后返回的是Movie[],这显然不匹配,但我们可以将其标记为一个async方法。对于async方法, 编译器会重写整个方法实现来表示一个异步任务,以后我们会来观察它是如何实现这点的。

async Task<Movie[]> QueryMoviesAsync(int year, int first, int count)
{
    var client = new WebClient();
    var url = String.Format(query, year, first, count);
    var data = client.DownloadString(new Uri(url));

    var movies =
        from entry in XDocument.Parse(data).Descendants(xs + "entry")
        let properties = entry.Element(xm + "properties")
        select new Movie
        {
            /* ... */
        };

    return movies.ToArray();
}

不过只做到这点还不够,我们的方法还没有异步化,这还是个同步任务。不过,如今在一个async方法中,我们有能力组合调用另一个 async方法,并异步地等待。这里使用了一个扩展方法DownloadStringTaskAsync,以后也会包含在框架中。这个方法返回 Task<string>类型,表示未来某一时刻将会得到一个string对象。于是在async方法中,我们使用一个新的await操作符 来等待其返回。

async Task<Movie[]> QueryMoviesAsync(int year, int first, int count)
{
    var client = new WebClient();
    var url = String.Format(query, year, first, count);
    var data = await client.DownloadStringTaskAsync(new Uri(url));

    var movies =
        from entry in XDocument.Parse(data).Descendants(xs + "entry")
        let properties = entry.Element(xm + "properties")
        select new Movie
        {
            /* ... */
        };

    return movies.ToArray();
}

在执行时,方法会执行到await操作符这里,并确保接下来的代码是在一个回调函数/continuation中执行的。编译器会在这里重写这个方法,就像为yield重写迭代器那样,于是我们就不需要做其他事情了,任务结束后自然会执行await后面的代码。

这里的美妙之处在于可以任意组合,对于LoadMovies方法来说,我们也可以将其转化为async方法,并await之前的QueryMoviesAsync方法返回。

async void LoadMoviesAsync(int year)
{
    resultsPanel.Children.Clear();
    statusText.Text = "";
    var pageSize = 10;
    var imageCount = 0;

    while (true)
    {
        var movies = await QueryMoviesAsync(year, imageCount, pageSize);
        if (movies.Length == 0) break;
        DisplayMovies(movies);
        imageCount += movies.Length;
    }

    statusText.Text = String.Format("{0} Titles", imageCount);
}

于是异步实现就这么完成了,代码和之前几乎完全一致。您可以看出,这使得我们在执行异步代码时保留原本的逻辑实现。

那么再为应用程序添加一点功能吧。首先是异常处理:

async void LoadMoviesAsync(int year)
{
    resultsPanel.Children.Clear();
    statusText.Text = "";
    var pageSize = 10;
    var imageCount = 0;

    try
    {
        while (true)
        {
            var movies = await QueryMoviesAsync(year, imageCount, pageSize);
            if (movies.Length == 0) break;
            DisplayMovies(movies);
            imageCount += movies.Length;
        }

        statusText.Text = String.Format("{0} Titles", imageCount);
    }
    catch (XmlException)
    {
        statusText.Text = "Data Error";
    }
}

我们无需分离代码或是逻辑,这一切都和同步代码完全一致。再来看看“取消(cancellation)”,对于async方法来说, 我们可以传递一个CancellationToken,表示任务需要监听这个对象的改变。如QueryMoviesAsync便可以增加一个参数:

async Task<Movie[]> QueryMoviesAsync(int year, int first, int count, CancellationToken ct)
{
    var client = new WebClient();
    var url = String.Format(query, year, first, count);
    var data = await client.DownloadStringTaskAsync(new Uri(url), ct);

    var movies =
        from entry in XDocument.Parse(data).Descendants(xs + "entry")
        let properties = entry.Element(xm + "properties")
        select new Movie
        {
            /* ... */
        };

    return movies.ToArray();
}

这样便得到了一个可取消的async方法。对于逻辑流来说,取消操作就相当于一个异常,代码里需要处理一个TaskCanceledException:

CancellationTokenSource cts;

async void LoadMoviesAsync(int year)
{
    resultsPanel.Children.Clear();
    statusText.Text = "";
    var pageSize = 10;
    var imageCount = 0;

    cts = new CancellationTokenSource();
    try
    {
        while (true)
        {
            var movies = await QueryMoviesAsync(year, imageCount, pageSize, cts.Token);
            if (movies.Length == 0) break;
            DisplayMovies(movies);
            imageCount += movies.Length;
        }
        statusText.Text = String.Format("{0} Titles", imageCount);
    }
    catch (TaskCanceledException) { }

    cts = null;
}

private void cancelButton_Click(object sender, RoutedEventArgs e)
{
    if (cts != null)
    {
        cts.Cancel();
        statusText.Text = "Canceled";
    }
}

那么超时又怎么说?超时其实就类似一段时间之后的取消。于是我们可以另写一个小方法来处理这个问题:

async void StartTimeoutAsync()
{
    await TaskEx.Delay(5000);
    if (cts != null)
    {
        cts.Cancel();
        statusText.Text = "Timeout";
    }
}

private void searchButton_Click(object sender, RoutedEventArgs e)
{
    LoadMoviesAsync(Int32.Parse(textBox.Text));
    StartTimeoutAsync();
}

第一步,我们先等待5秒钟,如果任务还在执行,那么我们就取消掉。所以无论是超时,取消还是错误处理,程序的逻辑结构都得以最大限度 的保留,就好比编写普通的代码一样。例如上面的Delay,看上去是顺序逻辑流,但实际上是异步的。为了表现出这点,我们可以为程序新加上一个有趣的功 能:

async void ShowDateTimeAsync()
{
    while (true)
    {
        Title = "Movie Finder " + DateTime.Now;
        await TaskEx.Delay(1000);
    }
}

public MainWindow()
{
    InitializeComponent();
    textBox.Focus();
    ShowDateTimeAsync();
}

于是在标题栏上便会每隔一秒刷新显示当前时间,与此同时搜索也好,超时也罢,在程序执行时UI都可以获得响应。

值得强 调的是,上面实现的这些功能都没有启用额外的线程,所有这些都在UI线程上执行。那么什么时候需要额外的线程呢?这便是计算密集型操作。例如这里我要执行 五千万次平方根计算,这需要耗费一段时间。不过这样的操作,对于UI线程来说,这也不过是一个异步操作,不是吗?启动操作,然后等待其完成,在它完成之后 再对结果做些处理:

async void ComputeStuffAsync()
{
    double result = 0;
    await TaskEx.Run(() =>
    {
        for (int i = 1; i < 500000000; i++)
        {
            result += Math.Sqrt(i);
        }
    });

    MessageBox.Show("The result is " + result, "Background Task",
        MessageBoxButton.OK, MessageBoxImage.Information);
}

private void searchButton_Click(object sender, RoutedEventArgs e)
{
    LoadMoviesAsync(Int32.Parse(textBox.Text));
    StartTimeoutAsync();
    ComputeStuffAsync();
}

TaskEx.Run方法会构造一个后台线程,并返回异步操作,我们使用await等待其返回,这体现了绝佳的组合能力。启动后在任务管理器中便会发现CPU占用率明显上升。

我在这里宣布,之前演示的技术预览版已经可以下载了。我们已经创建了C#和VB编译器的原型,并提供了一些示例。您可以在开发者中心下载,我在演讲最后会给出URL。

[转载]提示Cannot display this video mode(不能显示此种视频模式)的解决

mikel阅读(1943)

[转载]提示Cannot display this video mode(不能显示此种视频模式)的解决-中国电脑救援中心-国内最受关注的菜鸟资讯中心.

其实很多经验是需要总结的,这不换个显示器就出问题了,善用总结才能提高能力值嘛。

安装我的显示器时,屏幕显示”Cannot display this video mode”(“不能显示此种视频模式”),我该怎么办?
答: 飞利浦显示器之建议视频模式为:1280×1024@60赫兹。

1.拔出所有连接线,然后将电脑与你过去使用、显示正确的显示器连接。

2.在视窗“开始”菜单上,选择“设置”/“控制板”。在控制板窗口中,选择“显示”图标。在显示控制板中,选择“设置”插签。设置插签下,在标明“桌面区域”的方框中,将滑杆移至 1280×1024 像素 (19″)。

3.打开“高级特性”并将“刷新率”调至60赫兹,然后点击“OK”。

4.重新启动电脑,重复上述第三步骤,验明电脑已设置为1280×1024@60Hz (19″)。

5.关闭电脑,拆接旧显示器,重新连接新的飞利浦液晶显示器(Philips LCD)。

6.打开显示器,然后打开电脑。

[转载]闭包工厂模式:从Lambda到Object

mikel阅读(1075)

[转载]闭包工厂模式:从Lambda到Object – Thinking – 博客园.

下面的这段C#3.0代码看似再普通不过:

Stack stack = StackFactory.New();
stack.Push(1);
stack.Push(2);
stack.Push(3);
Console.WriteLine(stack.Pop());
Console.WriteLine(stack.Pop());
Console.WriteLine(stack.Pop());

运行结果:

>>3

>>2

>>1

但 如果我告诉你Stack并不是一个普通的class Stack,而是一个类型别名:using Stack = System.Func<T1, R1>,它其实是一个委托,你会不会觉得很神奇?说得更清楚一些,StackFatory.New()所创建的不是一个普通对象,而是创建了一个闭 包(Closure)。

闭包是什么?

那么闭包究竟是什么 呢?目前有两种流行的说法:一种说法是闭包是在其词法上下文中引用了自由变量的函数;另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。两 种说法都对,但我更倾向于第二种表述,因为它明确地将闭包定义为“实体”。从例子中我们可以看出,闭包可以表现出对象的特征,而普通的lambda函数或 delegate更像是某个方法。结合两种定义,我认为可以把闭包理解为带状态的函数。

自由变量

我们先来看一个闭包的简单例子:

static Func<int, int> AddX(int x) {
return (y) => x + y;
}

这里的lambda函数(y) => x + y就是一个闭包,它引用了其词法上下文之外的自由变量x。对AddX(8)求值将用8代换x,即(y)=>8 + y;若再继续求值AddX(8)(100)将得到8 + 100 = 108。

状态

下面我们将看到如何使闭包具有状态:

static Func<int> NewCounter() {
int x = 0;
return () => ++x;
}

Func<int> counter1 = NewCounter();
Console.WriteLine(counter1());
Console.WriteLine(counter1());
Console.WriteLine(counter1());

Func<int> counter2 = NewCounter();
Console.WriteLine(counter2());
Console.WriteLine(counter2());
Console.WriteLine(counter2());

运行结果:

>>1

>>2

>>3

>>1

>>2

>>3

我们通过NewCounter创建了一个闭包,每次对闭包进行求值都会使其环境的局部变量x增1,这样就体现了闭包的状态。同时,我们注意到局部变量x对于不同的闭包是独立的,counter1和counter2并不共享同一个x。

闭包 vs class

这里我们可以和OOP程序做一个对比,如果要用类来实现Counter我们会怎么做呢?

class Counter{ //对应NewCounter

private int x; //对应局部变量int x

public Counter() { x = 0; } //new Counter()对应NewCounter()

public int Count() { return ++x;} //对应() => ++x

}

和 上面的闭包程序相比,从结构上看是不是有些类似呢?Counter类与NewCounter函数对应;new Counter()与NewCounter()对应;Counter类的私有成员x和NewCounter的局部变量x对应;Counter类的 Count()方法与闭包对应。

行为

除了状态,我们还需要让闭包具备类似stack.Push()和stack.Pop()这样的对象行为。由于闭包只拥有一个()运算符,需要怎样做才能使其具有多个方法呢?答案是高阶函数(Higher-Order Function)。看刚才Stack的例子:

public enum Method {
Push, Pop, Top
}

public static Func<Method, object> New() {
LinkedList<int> aList = new LinkedList<int>();
Func<Method, object> func = (method) => {
switch (method) {
case Method.Push:
Action<int> push = (int aValue) => { aList.AddLast(aValue); };
return push;
case Method.Pop:
Func<int> pop = () => {
int value = aList.Last.Value;
aList.RemoveLast();
return value;
};
return pop;
case Method.Top:
Func<int> top = () => { return aList.Last.Value; };
return top;
default:
return null;
}
};
return func;
}

NewStack()返回的是一个Func<Method, object>类型的闭包,它的参数是enum Method类型的,返回值是object。NewStack()(Method.Push)将得到:

Action<int> push = (int aValue) => { aList.AddLast(aValue); };

这就是实际的Push方法!不过,在调用之前还需要显式转换一下类型:

(NewStack()(Method.Push) as Action<int>)(1);

最后,我们利用C#3.0的扩展方法(Extension Method)包装一下,让这个调用更加简洁:

public static void Push(this Func<Method, object> aStack, int aValue){
(aStack(Method.Push) as Action<int>)(aValue);
}

public static int Pop(this Func<Method, object> aStack){
return (int)(aStack(Method.Pop) as Func<int>)();
}

public static int Top(this Func<Method, object> aStack){
return (int)(aStack(Method.Top) as Func<int>)();
}

这样,我们就能写出stack.Push(1), stack.Pop()这样很OO的代码来了!通过这样一步步地探索,不知道您是否对函数式编程的闭包以及它和OOP对象的关系有了更深的理解呢?

模式

我们可以把上面在C#3.0中用闭包创建对象的方法归纳为一种固定的模式,不妨称其为闭包工厂模式(Closure Factory Pattern)。模式要点包括:

1. 定义一个工厂类XXFactory,提供创建闭包对象的静态工厂方法New;

2. 在New方法的内定义局部对象作为闭包对象的状态m_States;

3. 定义enum Method表示对象所具有的方法;

3. 在New方法内创建并返回一个引用m_States的闭包closureObject,其类型为Func<Method, object>;

4. closureObject接受Method参数,返回表示该方法的闭包(或普通委托)的methodObject;

5. 通过扩展方法为Func<Method, object>定义扩展方法,为closureObject的方法调用加上语法糖衣。

参考

闭包的概念、形式与应用

The Beauty of Closures