guochunyang2004

 

2011年8月30日

EasyHosts hosts文件管理工具

摘要: EasyHosts hosts文件管理工具 可对系统hosts文件方便添加、修改、删除、备份、恢复阅读全文

posted @ 2011-08-30 22:34 果然如此 阅读(83) 评论(0) 编辑

2011年1月20日

多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄

本篇从Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler的类关系图开始,希望通过本篇的介绍能对常见的线程同步方法有一个整体的认识,而对每种方式的使用细节,适用场合不会过多解释。让我们来看看这几个类的关系图:

 

      1.lock关键字

      lock是C#关键词,它将语句块标记为临界区,确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。方法是获取给定对象的互斥锁,执行语句,然后释放该锁。

      MSDN上给出了使用lock时的注意事项通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反此准则。

      1)如果实例可以被公共访问,将出现 lock (this) 问题。

      2)如果 MyType 可以被公共访问,将出现 lock (typeof (MyType)) 问题由于一个类的所有实例都只有一个类型对象(该对象是typeof的返回结果),锁定它,就锁定了该对象的所有实例。微软现在建议不要使用lock(typeof(MyType)),因为锁定类型对象是个很缓慢的过程,并且类中的其他线程、甚至在同一个应用程序域中运行的其他程序都可以访问该类型对象,因此,它们就有可能代替您锁定类型对象,完全阻止您的执行,从而导致你自己的代码的挂起。

      3)由于进程中使用同一字符串的任何其他代码将共享同一个锁,所以出现 lock(“myLock”) 问题。这个问题和.NET Framework创建字符串的机制有关系,如果两个string变量值都是"myLock",在内存中会指向同一字符串对象。

      最佳做法是定义 private 对象来锁定, 或 private static对象变量来保护所有实例所共有的数据。

      我们再来通过IL Dasm看看lock关键字的本质,下面是一段简单的测试代码:

    lock (lockobject)
    {
        int i = 5;
    }
      用IL Dasm打开编译后的文件,上面的语句块生成的IL代码为:

      IL_0045:  call       void [mscorlib]System.Threading.Monitor::Enter(object)
      IL_004a:  nop
      .try
      {
        IL_004b:  nop
        IL_004c:  ldc.i4.5
        IL_004d:  stloc.1
        IL_004e:  nop
        IL_004f:  leave.s    IL_0059
      }  // end .try
      finally
      {
        IL_0051:  ldloc.3
        IL_0052:  call       void [mscorlib]System.Threading.Monitor::Exit(object)
        IL_0057:  nop
        IL_0058:  endfinally
      }  // end handler
      通过上面的代码我们很清楚的看到:lock关键字其实就是对Monitor类的Enter()和Exit()方法的封装,并通过try...catch...finally语句块确保在lock语句块结束后执行Monitor.Exit()方法,释放互斥锁。

      2.Monitor类

      Monitor类通过向单个线程授予对象锁来控制对对象的访问。对象锁提供限制访问临界区的能力。当一个线程拥有对象的锁时,其他任何线程都不能获取该锁。还可以使用 Monitor 来确保不会允许其他任何线程访问正在由锁的所有者执行的应用程序代码节,除非另一个线程正在使用其他的锁定对象执行该代码。

      通过对lock关键字的分析我们知道,lock就是对Monitor的Enter和Exit的一个封装,而且使用起来更简洁,因此Monitor类的Enter()和Exit()方法的组合使用可以用lock关键字替代。

      另外Monitor类还有几个常用的方法:

      TryEnter()能够有效的解决长期死等的问题,如果在一个并发经常发生,而且持续时间长的环境中使用TryEnter,可以有效防止死锁或者长时间的等待。比如我们可以设置一个等待时间bool gotLock = Monitor.TryEnter(myobject,1000),让当前线程在等待1000秒后根据返回的bool值来决定是否继续下面的操作。

      Wait()释放对象上的锁以便允许其他线程锁定和访问该对象。在其他线程访问对象时,调用线程将等待。脉冲信号用于通知等待线程有关对象状态的更改。

      Pulse(),PulseAll()向一个或多个等待线程发送信号。该信号通知等待线程锁定对象的状态已更改,并且锁的所有者准备释放该锁。等待线程被放置在对象的就绪队列中以便它可以最后接收对象锁。一旦线程拥有了锁,它就可以检查对象的新状态以查看是否达到所需状态。

      注意:Pulse、PulseAll和Wait方法必须从同步的代码块内调用。

      我们假定一种情景:妈妈做蛋糕,小孩有点馋,妈妈每做好一块就要吃掉,妈妈做好一块后,告诉小孩蛋糕已经做好了。下面的例子用Monitor类的Wait和Pulse方法模拟小孩吃蛋糕的情景。

Wait和Pulse的示例
    //仅仅是说明Wait和Pulse/PulseAll的例子
    //逻辑上并不严密,使用场景也并不一定合适
    class MonitorSample
    {
        private int n = 1;  //生产者和消费者共同处理的数据
        private int max = 10000;

        private object monitor = new object();

        public void Produce()
        {
            lock (monitor)
            {
                for (; n <= max; n++)
                {
                    Console.WriteLine("妈妈:第" + n.ToString() + "块蛋糕做好了");
                    //Pulse方法不用调用是因为另一个线程中用的是Wait(object,int)方法
                    //该方法使被阻止线程进入了同步对象的就绪队列
                    //是否需要脉冲激活是Wait方法一个参数和两个参数的重要区别
                    //Monitor.Pulse(monitor);
                    //调用Wait方法释放对象上的锁并阻止该线程(线程状态为WaitSleepJoin)
                    //该线程进入到同步对象的等待队列,直到其它线程调用Pulse使该线程进入到就绪队列中
                    //线程进入到就绪队列中才有条件争夺同步对象的所有权
                    //如果没有其它线程调用Pulse/PulseAll方法,该线程不可能被执行
                    Monitor.Wait(monitor);
                }
            }
        }

        public void Consume()
        {
            lock (monitor)
            {
                while (true)
                {
                    //通知等待队列中的线程锁定对象状态的更改,但不会释放锁
                    //接收到Pulse脉冲后,线程从同步对象的等待队列移动到就绪队列中
                    //注意:最终能获得锁的线程并不一定是得到Pulse脉冲的线程
                    Monitor.Pulse(monitor);
                    //释放对象上的锁并阻止当前线程,直到它重新获取该锁
                    //如果指定的超时间隔已过,则线程进入就绪队列
                    Monitor.Wait(monitor,1000);
                    Console.WriteLine("孩子:开始吃第" + n.ToString() + "块蛋糕");
                }
            }
        }

        static void Main(string[] args)
        {
            MonitorSample obj = new MonitorSample();
            Thread tProduce = new Thread(new ThreadStart(obj.Produce));
            Thread tConsume = new Thread(new ThreadStart(obj.Consume));
            //Start threads.
            tProduce.Start();
            tConsume.Start();

            Console.ReadLine();
        }
    }
      这个例子的目的是要理解Wait和Pulse如何保证线程同步的,同时要注意Wait(obeject)和Wait(object,int)方法的区别,理解它们的区别很关键的一点是要理解同步的对象包含若干引用,其中包括对当前拥有锁的线程的引用、对就绪队列(包含准备获取锁的线程)的引用和对等待队列(包含等待对象状态更改通知的线程)的引用。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/mengsuo/archive/2008/12/06/3460095.aspx

posted @ 2011-01-20 11:43 果然如此 阅读(171) 评论(0) 编辑

2007年1月28日

.NET大文件上传知识

 

最近做在做ePartner项目,涉及到文件上传的问题。 以前也做过文件上传,但都是些小文件,不超过2M。 这次要求上传100M以上的东西。 没办法找来资料研究了一下。基于WEB的文件上传可以使用FTPHTTP两种协议,用FTP的话虽然传输稳定,但安全性是个严重的问题,而且FTP服务器读用户库获取权限,这样对于用户使用来说还是不太方便。 剩下只有HTTP。在HTTP中有3种方式,PUTWEBDAVRFC1867,前2种方法不适合大文件上传,目前我们使用的web上传都是基于RFC1867标准的HTML中基于表单的文件上传。

 

一、先简要介绍一下RFC1867Form-based File Upload in HTML)标准:

1.带有文件提交功能的HTML表单

现有的HTML规范为INPUT元素的TYPE属性定义了八种可能的值,分别是:CHECKBOX, HIDDEN, IMAGE, PASSWORD, RADIO, RESET, SUBMIT, TEXT. 另外,当表单采用POST方式的时候,表单默认的具有"application/x-www-form-urlencoded" ENCTYPE属性。

 

RFC1867标准对HTML做出了两处修改:

1)为INPUT元素的TYPE属性增加了一个FILE选项。

2INPUT标记可以具有ACCEPT属性,该属性能够指定可被上传的文件类型或文件格式列表。

 

另外,本标准还定义了一种新的MIME类型:multipart/form-data,以及当处理一个带有ENCTYPE="multipart/form-data" 并且/或含有<INPUT type="file">的标记的表单时所应该采取的行为。

 

举例来说,当HTML表单作者想让用户能够上传一个或更多的文件时,他可以这么写:

 

    <FORM ENCTYPE="multipart/form-data" ACTION="_URL_" METHOD=POST>

File to process:
<INPUT NAME="userfile1" TYPE="file">

            <INPUT TYPE="submit" VALUE="Send File">

    </FORM>

 

HTML DTD里所需要做出的改动是为InputType实体增加一个选项。此外,我们也建议用一系列用逗号分隔的文件类型来作为INPUT标记的ACCEPT属性。

 

  ... (其他元素) ...

 

 <!ENTITY % InputType "(TEXT | PASSWORD | CHECKBOX |

                         RADIO | SUBMIT | RESET |

                         IMAGE | HIDDEN | FILE )">

 <!ELEMENT INPUT - 0 EMPTY>

 <!ATTLIST INPUT

          TYPE %InputType TEXT

          NAME CDATA #IMPLIED -- required for all but submit and reset

          VALUE CDATA #IMPLIED

          SRC %URI #IMPLIED -- for image inputs --

          CHECKED (CHECKED) #IMPLIED

          SIZE CDATA #IMPLIED --like NUMBERS,

                                  but delimited with comma, not space

          MAXLENGTH NUMBER #IMPLIED

          ALIGN (top|middle|bottom) #IMPLIED

          ACCEPT CDATA #IMPLIED --list of content types

          >

 

 ... (其他元素) ...

 

2.文件传输延迟

在某些情况下,在确实准备接受数据前,服务器先对表单数据中的某些元素(比如说用户名,账号等)进行验证是推荐的做法。但是,经过一定的考虑后,我们认为如果服务器想这样做的话,最好是采用一系列的表单,并将前面所验证过的数据元素作为“隐藏”字段传回给客户端,或者是通过安排表单使那些需要验证的元素先显示出来。这样的话,那些需要做复杂的应用的服务器可以自己维持事务处理的状态,而那些简单的应用的则可以实现得简单些。

 

HTTP协议可能需要知道整个事务处理中的内容总长度。即使没有明确要求,HTTP客户端也应该提供上传的所有文件的内容总长度,这样一个繁忙的服务器就能够判断文件的内容是否是过大以至于将不能完整地处理,从而返回一个错误代码并关闭该连接,而不用等到接受了所有的数据才进行判断。目前一些现有的CGI应用对所有的POST事务都需要知道内容总长度。

 

如果INPUT标记含有一个MAXLENGTH属性,客户端可以将这个属性值看作是服务器端所能够接受的传送文件的最大字节数。在这种情况下,服务器能够在上传开始前,提示客户端在服务器上有多少空间可以用来进行文件上传。但是应该引起注意的是,这仅仅是一个提示,在表单被创建后和文件上传前,服务器的实际需求可能会发生改变。

 

在任何情况下,如果接受的文件过大的话,任何一个HTTP服务器都有可能在文件传输的过程中中断传输。

 

3.传输二进制数据的其他解决办法

有些人曾经建议使用一种新的MIME类型"aggregate",比如说aggregate/mixed 或是content-transfer-encoding ""来描述那些不确定长度的二进制数据,而不是靠分解为多个部分来表示。虽然我们并不反对这么做,但这需要增加额外的设计和标准化工作来让大家接受并理解"aggregate"。 从另一方面来说,"分解为多部分"的机制工作得很好,能够非常简单的在客户发送端和服务器接受端加以实现,而且能像其他一些综合处理二进制数据的方式一样高效率地工作。

 

4.例子

假设服务器段提供的是如下的HTML

     <FORM ACTION="http://server.dom/cgi/handle"

           ENCTYPE="multipart/form-data"

           METHOD=POST>

     What is your name? <INPUT TYPE=TEXT NAME=submitter>

     What files are you sending? <INPUT TYPE=FILE NAME=pics>

     </FORM>

用户在“姓名”字段里面填写"Joe Blow",对问题'What files are you sending?',用户选择

了一个文本文件"file1.txt"

客户段可能发送回如下的数据:

        Content-type: multipart/form-data, boundary=AaB03x

 

        --AaB03x

        content-disposition: form-data; name="field1"

 

        Joe Blow

        --AaB03x

        content-disposition: form-data; name="pics"; filename="file1.txt"

        Content-Type: text/plain

 

         ... file1.txt 的内容...

        --AaB03x--

如果用户同时还选择了另一个图片文件"file2.gif",那么客户端可能发送的数据将是:

        Content-type: multipart/form-data, boundary=AaB03x

 

        --AaB03x

        content-disposition: form-data; name="field1"

 

        Joe Blow

        --AaB03x

        content-disposition: form-data; name="pics"

        Content-type: multipart/mixed, boundary=BbC04y

 

        --BbC04y

        Content-disposition: attachment; filename="file1.txt"

 

        Content-Type: text/plain

 

        ... file1.txt 的内容...

        --BbC04y

        Content-disposition: attachment; filename="file2.gif"

        Content-type: image/gif

        Content-Transfer-Encoding: binary

 

          ... file2.gif的内容...

        --BbC04y--

        --AaB03x--

    二、利用RFC1867标准处理文件上传的两种方式:

         1.一次性得到上传的数据,然后分析处理。

看了N多代码之后发现,目前无组件程序和一些COM组件都是使用Request.BinaryRead方法。一次性得到上传的数据,然后分析处理。这就是为什么上传大文件很慢的原因了,IIS超时不说,就算几百M文件上去了,分析处理也得一阵子。

         2.一边接收文件,一边写硬盘。

 

了解了一下国外的商业组件,比较流行的有Power-WebAspUpload,ActiveFile,ABCUpload,aspSmartUpload,SA-FileUp。其中比较优秀的是ASPUPLOADSA-FILE,他们号称可以处理2G的文件(SA-FILE EE版甚至没有文件大小的限制),而且效率也是非常棒,难道编程语言的效率差这么多?查了一些资料,觉得他们都是直接操作文件流。这样就不受文件大小的制约。但老外的东西也不是绝对完美,ASPUPLOAD处理大文件后,内存占用情况惊人。1G左右都是稀松平常。至于SA-FILE虽然是好东西但是破解难寻。然后发现2.NET上传组件,Lion.Web.UpLoadModuleAspnetUpload也是操作文件流。但是上传速度和CPU占用率都不如老外的商业组件。

做了个测试,LAN内传1G的文件。ASPUPLOAD上传速度平均是4.4M/sCPU占用10-15,内存占用700MSA-FILE也差不多这样。而AspnetUpload最快也只有1.5M/s,平均是700K/sCPU占用15-39,测试环境: PIII800,256M内存,100M LAN。我想AspnetUpload速度慢是可能因为一边接收文件,一边写硬盘。资源占用低的代价就是降低传输速度。但也不得不佩服老外的程序,CPU占用如此之低.....

             

     三、ASP.NET上传文件遇到的问题

我们在用ASP.NET上传大文件时都遇到过这样或那样的问题。设置很大的maxRequestLength值并不能完全解决问题,因为ASP.NETblock直到把整个文件载入内存后,再加以处理。实际上,如果文件很大的话,我们经常会见到Internet Explorer显示 "The page cannot be displayed - Cannot find server or DNS Error",好像是怎么也catch不了这个错误。为什么?因为这是个client side错误,server side端的Application_Error是处理不到的。

     四、ASP.NET大文件上传解决方案

解决的方法是利用隐含的HttpWorkerRequest,用它的GetPreloadedEntityBody ReadEntityBody方法从IISASP.NET建立的pipe里分块读取数据。Chris Hynes为我们提供了这样的一个方案(HttpModule),该方案除了允许你上传大文件外,还能实时显示上传进度。

         Lion.Web.UpLoadModuleAspnetUpload 两个.NET组件都是利用的这个方案。

 

         方案原理:

利用HttpHandler实现了类似于ISAPI Extention的功能,处理请求(Request)的信息和发送响应(Response)

 

方案要点:

1.   httpHandler or HttpModule
a.
asp.net进程处理request请求之前截获request对象
b.
分块读取和写入数据
c.
实时跟踪上传进度更新meta信息

2.   利用隐含的HttpWorkerRequest用它的GetPreloadedEntityBody ReadEntityBody方法处理文件流
IServiceProvider provider = (IServiceProvider) HttpContext.Current;
  HttpWorkerRequest wr = (HttpWorkerRequest) provider.GetService(typeof(HttpWorkerRequest));
  byte[] bs = wr.GetPreloadedEntityBody();
  ....
  if (!wr.IsEntireEntityBodyIsPreloaded())
  {
        int n = 1024;
        byte[] bs2 = new byte[n];
        while (wr.ReadEntityBody(bs2,n) >0)
       {
             .....
        }
  }

3.   自定义Multipart MIME 解析器
自动截获MIME分割符
将文件分块写如临时文件

实时更新Appliaction 状态(ReceivingData, Error, Complete

posted @ 2007-01-28 19:29 果然如此 阅读(500) 评论(0) 编辑

导航

统计

公告

昵称:果然如此
园龄:5年
粉丝:0
关注:0

搜索

 
 

常用链接

我的标签

随笔档案

最新评论