开发学院WEB开发ASP.NET ASP.NET性能优化 阅读

ASP.NET性能优化

 2010-10-31 08:59:55 来源:WEB开发网   
核心提示:避免锁定 this 锁定 this 会影响该实例的所有方法,假设对象 obj 有 A 和 B 两个方法,ASP.NET性能优化(4),其中 A 方法使用 lock(this) 对方法中的某段代码设置同步保护,现在,可以无需考虑优化问题,注意 typeof 运算符比对象实例上的 GetType 方法要快,因为某种原因,B
 避免锁定 this
  锁定 this 会影响该实例的所有方法。假设对象 obj 有 A 和 B 两个方法,其中 A 方法使用 lock(this) 对方法中的某段代码设置同步保护。现在,因为某种原因,B 方法也开始使用 lock(this) 来设置同步保护了,并且可能为了完全不同的目的。这样,A 方法就被干扰了,其行为可能无法预知。所以,作为一种良好的习惯,建议避免使用 lock(this) 这种方式。
 使用为同步目的专门构造的成员变量
 这是推荐的做法。方式就是 new 一个 object 对象, 该对象仅仅用于同步目的。
 如果有多个方法都需要同步,并且有不同的目的,那么就可以为些分别建立几个同步成员变量。

1.3.1.4 集合同步
 C#为各种集合类型提供了两种方便的同步机制:Synchronized 包装器和 SyncRoot 属性。
// Creates and initializes a new ArrayList
ArrayList myAL = new ArrayList();
myAL.Add( " The " );
myAL.Add( " quick " );
myAL.Add( " brown " );
myAL.Add( " fox " );

// Creates a synchronized wrapper around the ArrayList
ArrayList mySyncdAL = ArrayList.Synchronized(myAL);


调用 Synchronized 方法会返回一个可保证所有操作都是线程安全的相同集合对象。考虑 mySyncdAL[0] = mySyncdAL[0] + "test" 这一语句,读和写一共要用到两个锁。一般讲,效率不高。推荐使用 SyncRoot 属性,可以做比较精细的控制。

1.3.2 使用 ThreadStatic 替代 NameDataSlot ★
 存取 NameDataSlot 的 Thread.GetData 和 Thread.SetData 方法需要线程同步,涉及两个锁:一个是 LocalDataStore.SetData 方法需要在 AppDomain 一级加锁,另一个是 ThreadNative.GetDomainLocalStore 方法需要在 Process 一级加锁。如果一些底层的基础服务使用了 NameDataSlot,将导致系统出现严重的伸缩性问题。
 规避这个问题的方法是使用 ThreadStatic 变量。示例如下:public sealed class InvokeContext
{
[ThreadStatic]
private static InvokeContext current;
private Hashtable maps = new Hashtable();
}


1.3.3 多线程编程技巧
 1.3.3.1 使用 Double Check 技术创建对象internal IDictionary KeyTable
{
get
{
if ( this ._keyTable == null )
{
lock ( base ._lock)
{
if ( this ._keyTable == null )
{
this ._keyTable = new Hashtable();
}
}
}
return this ._keyTable;
}
}


创建单例对象是很常见的一种编程情况。一般在 lock 语句后就会直接创建对象了,但这不够安全。因为在 lock 锁定对象之前,可能已经有多个线程进入到了第一个 if 语句中。如果不加第二个 if 语句,则单例对象会被重复创建,新的实例替代掉旧的实例。如果单例对象中已有数据不允许被破坏或者别的什么原因,则应考虑使用 Double Check 技术。

1.4 类型系统
 1.4.1 避免无意义的变量初始化动作
 CLR保证所有对象在访问前已初始化,其做法是将分配的内存清零。因此,不需要将变量重新初始化为0、false或null。
 需要注意的是:方法中的局部变量不是从堆而是从栈上分配,所以C#不会做清零工作。如果使用了未赋值的局部变量,编译期间即会报警。不要因为有这个印象而对所有类的成员变量也做赋值动作,两者的机理完全不同!
 1.4.2 ValueType 和 ReferenceType

1.4.2.1 以引用方式传递值类型参数
 值类型从调用栈分配,引用类型从托管堆分配。当值类型用作方法参数时,默认会进行参数值复制,这抵消了值类型分配效率上的优势。作为一项基本技巧,以引用方式传递值类型参数可以提高性能。
 1.4.2.2 为 ValueType 提供 Equals 方法
  .net 默认实现的 ValueType.Equals 方法使用了反射技术,依靠反射来获得所有成员变量值做比较,这个效率极低。如果我们编写的值对象其 Equals 方法要被用到(例如将值对象放到 HashTable 中),那么就应该重载 Equals 方法。public struct Rectangle
{
public double Length;
public double Breadth;
public override bool Equals ( object ob)
{
if (ob is Rectangle)
return Equels ((Rectangle)ob))
else
return false ;
}
private bool Equals (Rectangle rect)
{
return this .Length == rect.Length && this .Breadth == rect.Breach;
}
}


1.4.2.3 避免装箱和拆箱
 C#可以在值类型和引用类型之间自动转换,方法是装箱和拆箱。装箱需要从堆上分配对象并拷贝值,有一定性能消耗。如果这一过程发生在循环中或是作为底层方法被频繁调用,则应该警惕累计的效应。
 一种经常的情形出现在使用集合类型时。例如:ArrayList al = new ArrayList();
for ( int i = 0 ; i < 1000 ; i ++ )
{
al.Add(i); // Implicitly boxed because Add() takes an object
}
int f = ( int )al[ 0 ]; // The element is unboxed


1.5 异常处理
 异常也是现代语言的典型特征。与传统检查错误码的方式相比,异常是强制性的(不依赖于是否忘记了编写检查错误码的代码)、强类型的、并带有丰富的异常信息(例如调用栈)。
 1.5.1 不要吃掉异常★
 关于异常处理的最重要原则就是:不要吃掉异常。这个问题与性能无关,但对于编写健壮和易于排错的程序非常重要。这个原则换一种说法,就是不要捕获那些你不能处理的异常。
 吃掉异常是极不好的习惯,因为你消除了解决问题的线索。一旦出现错误,定位问题将非常困难。除了这种完全吃掉异常的方式外,只将异常信息写入日志文件但并不做更多处理的做法也同样不妥。
 1.5.2 不要吃掉异常信息★
 有些代码虽然抛出了异常,但却把异常信息吃掉了。
 为异常披露详尽的信息是程序员的职责所在。如果不能在保留原始异常信息含义的前提下附加更丰富和更人性化的内容,那么让原始的异常信息直接展示也要强得多。千万不要吃掉异常。
 1.5.3 避免不必要的抛出异常
 抛出异常和捕获异常属于消耗比较大的操作,在可能的情况下,应通过完善程序逻辑避免抛出不必要不必要的异常。与此相关的一个倾向是利用异常来控制处理逻辑。尽管对于极少数的情况,这可能获得更为优雅的解决方案,但通常而言应该避免。
 1.5.4 避免不必要的重新抛出异常
 如果是为了包装异常的目的(即加入更多信息后包装成新异常),那么是合理的。但是有不少代码,捕获异常没有做任何处理就再次抛出,这将无谓地增加一次捕获异常和抛出异常的消耗,对性能有伤害。

1.6 反射
 反射是一项很基础的技术,它将编译期间的静态绑定转换为延迟到运行期间的动态绑定。在很多场景下(特别是类框架的设计),可以获得灵活易于扩展的架构。但带来的问题是与静态绑定相比,动态绑定会对性能造成较大的伤害。
 1.6.1 反射分类
  type comparison :类型判断,主要包括 is 和 typeof 两个操作符及对象实例上的 GetType 调用。这是最轻型的消耗,可以无需考虑优化问题。注意 typeof 运算符比对象实例上的 GetType 方法要快,只要可能则优先使用 typeof 运算符。

上一页  1 2 3 4 5 6 7 8  下一页

Tags:ASP.NET 性能优化

编辑录入:爽爽 [复制链接] [打 印]
[]
  • 好
  • 好的评价 如果觉得好,就请您
      0%(0)
  • 差
  • 差的评价 如果觉得差,就请您
      0%(0)
赞助商链接