Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1130296
  • 博文数量: 170
  • 博客积分: 1603
  • 博客等级: 上尉
  • 技术积分: 1897
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-09 15:54
文章分类

全部博文(170)

文章存档

2016年(27)

2015年(21)

2014年(27)

2013年(21)

2012年(7)

2011年(67)

我的朋友

分类: C#/.net

2014-02-18 10:15:15

转载自

http://181796968.blog.51cto.com/213419/41776

今天同事问我 直接 try 插入速度快还是 搜索插入速度快-0-,网上找了个最靠谱的答案
数据库内部处理逻辑直接插入和搜索后再插入估计差不多,都会自己先搜索,然后返回数据或者错误

程序方面try catch就有效率区别了

所以我的基本结论就是,如果很很少出现catch,那么就直接try catch(虽然这文是说c#的但是估计大部分编译语言的处理方式都差不多)
如果重复可能比较多,还是先select,文章我也复制过来

Net出现多年之后还是对异常处理一知半解的,有很多误解,本文将讲解三个常见误解,一个是catch的使用方法是否正确,另外两个是try/catch的性能损失问题。 
  catch(Exception e)  
  {  
      throw e;  
  }  
  首先说明,这不是一个错误用法,但是通常来讲,我们应该避免这种代码。然后要说明的是,这段代码有一个比较典型的作用就是改变异常出现的位置,也就是可以对某类异常统一在一个位置处理。先看下面代码:
      public int GetAllCount2()  
      {  
          try  
          {  
              openDB();  
              int i = 1;  
              return i;  
          }  
          catch (SqlException sex)  
          {  
              throw sex;  
          }  
          catch (Exception ex)  
          {  
              throw ex;  
          }  
      }  
      public int GetAllCount()  
      {  
          openDB(); // 这里也可能是微软企业类库等  
          int i = 1;  
          return i;  
      }  
    
      private void openDB()  
      {  
          conn.Open();  
      }  
有些人认为下面代码就是一个catch的错误用法:
     假设我们有一个公用方法叫openDB(),而很多方法中调用它,当数据库打开失败的时候,对于调用GetAllCount方法,异常将定位于 conn.Open而如果调用GetAllCount2,那么异常定位于throw sex的位置,同时堆栈信息也有所不同,可以更快捷的找到调用方法的位置,也可在此位置进行一些错误恢复处理。尤其是我们编写一些底层类库的时候,比如 Framework类库从不会把异常代码定位到Framework类库内部的某个方法上面。但是需要注意的是我们尽量避免捕获异常而不返回,例如 catch(){}
  这样的使用就是典型的错误使用了,因为对于Framework来讲,任何时候系统都可能抛出一个StackOverflowException或者OutOfMemoryExcetpion而上面这段代码则隐藏了这些异常,有时候则导致一些严重的问题。
  对于异常处理,在性能上有2点注意
  第一点,在使用try/catch时,如果不发生异常,那么几乎可以忽略性能的损失。
  关于这一点,这里我们进行一些深入分析,对此比较了解的可以跳过本节。首先,让我们先看一下try/catch的IL表现。我们有2个方法,一个使用try/catch,而另一个未做任何处理:
  static int Test1(int a, int b)  
  {  
      try  
      {  
          if (a > b)  
              return a;  
          return b;  
      }  
      catch  
      {  
          return -1;  
      }  
  }  
    
  static int Test2(int a, int b)  
  {  
      if (a > b)  
          return a;  
      return b;  
  }  
  使用ILDasm工具查看,IL代码分别如下:(这里之所以引入IL,是因为IL是比较接近机器汇编,所以在IL中我们可以更清楚的了解代码的执行情况,对IL没有兴趣的可以跳过此节)
  .method private hidebysig static int32  Test1(int32 a,  
                                                int32 b) cil managed  
  {  
    // 代码大小       30 (0x1e)  
    .maxstack  2  
    .locals init ([0] int32 CS$1$0000,  
             [1] bool CS$4$0001)  
    IL_0000:  nop  
    .try  
    {  
      IL_0001:  nop  
      IL_0002:  ldarg.0  
      IL_0003:  ldarg.1  
      IL_0004:  cgt  
      IL_0006:  ldc.i4.0  
      IL_0007:  ceq  
      IL_0009:  stloc.1  
      IL_000a:  ldloc.1  
      IL_000b:  brtrue.s   IL_0011  
      IL_000d:  ldarg.0  
      IL_000e:  stloc.0  
      IL_000f:  leave.s    IL_001b  
      IL_0011:  ldarg.1  
      IL_0012:  stloc.0  
      IL_0013:  leave.s    IL_001b  
    }  // end .try  
    catch [mscorlib]System.Object   
    {  
      IL_0015:  pop  
      IL_0016:  nop  
      IL_0017:  ldc.i4.m1  
      IL_0018:  stloc.0  
      IL_0019:  leave.s    IL_001b  
    }  // end handler  
    IL_001b:  nop  
    IL_001c:  ldloc.0  
    IL_001d:  ret  
  } // end of method Program::Test1  
    
  Test2  
    
  .method private hidebysig static int32  Test2(int32 a,  
                                                int32 b) cil managed  
  {  
    // 代码大小       22 (0x16)  
    .maxstack  2  
    .locals init ([0] int32 CS$1$0000,  
             [1] bool CS$4$0001)  
    IL_0000:  nop  
    IL_0001:  ldarg.0  
    IL_0002:  ldarg.1  
    IL_0003:  cgt  
    IL_0005:  ldc.i4.0  
    IL_0006:  ceq  
    IL_0008:  stloc.1  
    IL_0009:  ldloc.1  
    IL_000a:  brtrue.s   IL_0010  
    IL_000c:  ldarg.0  
    IL_000d:  stloc.0  
    IL_000e:  br.s       IL_0014  
    IL_0010:  ldarg.1  
    IL_0011:  stloc.0  
    IL_0012:  br.s       IL_0014  
    IL_0014:  ldloc.0  
    IL_0015:  ret  
  } // end of method Program::Test2  
 
这里我们只需关注红字高亮的几行即可。此处我们只关心try区块,即未发生异常的时候,对于Test1来讲,IL代码多出了8个字节来保存catch的处理代码,这一点对性能和资源几乎是微不足道的。
  我们看到当Test1执行到IL_000f或者IL_0013的时候,将数据出栈并使用leave.s退出try区块转向IL_001b地址,然后将数据入栈并返回。
  对于Test2来讲,执行到IL_000e或者IL_0012的时候, 直接退出,并将数据入栈然后返回。
  这里对几个关键指令简单介绍一下
  nop      do noting  
  stloc.0  Pop value from stack into local variable 0.  
  ldloc.0  Load local variable 0 onto stack.  
  br.s target branch to target, short form  
  leave.s target Exit a protected region of code, short form  
  下面我们看代码的实际运行情况,新建一个控制台Console程序,加入下面代码:
  static void Main(string[] args)  
  {  
      int times = 1000000;    //我们将结果放大100,0000倍  
      long l1, l2,l3,l4, s1, s2;  
    
      Console.WriteLine("Press any key to continue");  
      Console.Read();  
    
      for (int j = 0; j < 10; j++)  
      {  
          l1 = DateTime.Now.Ticks;  
    
          for (int i = 0; i < times; i++)  
              Test2(2, 4);  
    
          l2 = DateTime.Now.Ticks;  
          s1 = l2 - l1;  
          Console.WriteLine("time spent:" + s1);  
    
          l3 = DateTime.Now.Ticks;  
    
          for (int i = 0; i < times; i++)  
              Test1(2, 4);  
    
          l4 = DateTime.Now.Ticks;  
          s2 = l4 - l3;  
          Console.WriteLine("time spent:" + s2);  
     Console.WriteLine("difference:"+(s2-s1)+",rate:"+(float)(s2-s1)/s1/times);       }  
  }  
    
  static int Test1(int a, int b)  
  {  
      try  
      {  
          for (int i = 0; i < 100; i++) ;  // 模拟长时操纵  
          if (a > b)  
              return a;  
          return b;  
      }  
      catch  
      {  
          return -1;  
      }  
  }  
    
  static int Test2(int a, int b)  
  {  
      for (int i = 0; i < 100; i++) ;  // 模拟长时操纵  
      if (a > b)  
          return a;  
      return b;  
  }  
运行后可以看到代码的差异,通常在0.0001%的差别以内。
  第二点,如果发生异常,那么引发或处理异常时,将使用大量的系统资源和执行时间。引发异常只是为了处理确实异常的情况,而不是为了处理可预知的事件或流控制。例如,如果方法参数无效,而应用程序需要使用有效的参数调用方法,则可以引发异常。无效的方法参数意味着出现了异常情况。相反,用户偶尔会输入无效数据,这是可以预见的,因此如果用户输入无效,则不要引发异常。在这种情况下,请提供重试机制以便用户输入有效输入。
  我们经常需要将一个字符串转换为int,比如将Request.QueryString["id"]这样的字符串转换为int,在asp.net 1.x时代,我们常使用下列方式
  try  
  {  
      int id = Int32.Parse("123");  
  }  
  catch(){}  
  这样的后果是如果出现转换异常,你将不得不牺牲大量的系统资源来处理异常,即使你没有编写任何异常处理代码。
  当然你也可以编写大量的代码来检测和转换字符串来替代try/catch方式,而从asp.net 2.0以后,框架将这个检测转换过程封装到Int32.TryParse方法中,再也不用蹩脚的try/catch来处理了。
  还要补充一点,就是finally中的代码是始终保证运行的,所以留给大家一个问题,下面代码执行后a的值是多少:
  int a = 2;  
  try  
  {  
      int i = Int32.Parse("s");  
  }  
  catch  
  {  
      a = 1;  
      return;  
  }  
  finally  
  {  
      a = 3;  
  }  
  小节:本文不一定是完美的,如有遗漏之处还请指出。

阅读(1227) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~