谨防静态变量
class Counter {
private static int s_Number = 0;
public static int GetNextNumber () {
int newNumber = s_Number;
// DO SOME STUFF
s_Number = newNumber + 1;
return newNumber;
}
}
如果同时有两个线程同时调用GetNextNumber()方法并同时为newNumber分配同样的变量在s_Num前。
那么两个线程同时将得到同样的返回值。为了解决这个问题,你需要去锁定一部分的代码块,使得竞争线程进入一个等待队列但是这样会降低效率。
class Counter {
private static int s_Number = 0;
public static int GetNextNumber () {
lock (typeof(Counter)) {
int newNumber = s_Number;
// DO SOME STUFF
newNumber += 1;
s_Number = newNumber;
return newNumber;
}
}
}
谨防静态变量2接下来我们要关注引用类型的静态变量。记住,任何被根引用的对象都不能被清除。下面是一段代码:
class Olympics {
public static Collection<Runner> TryoutRunners;
}
class Runner {
private string _fileName;
private FileStream _fStream;
public void GetStats () {
FileInfo fInfo = new FileInfo(_fileName);
_fStream = _fileName.OpenRead();
}
}
因为Collection是存储Olympics类的静态集合,所以集合内的对象不会被垃圾回收器释放(因为它们都被root间接引用)。但是你可能要注意,每一次我们都要运行GetStats()来获取被打开文件流的状态。因为它们不能被关闭也不能被垃圾回收器释放而一直等待在那。想象一下我们如果有100000这样的对象存在,那么程序的性能就变得有多差。 单件
通过某种方式我们可以永久的保持一个对象实例在内存中。我们通过使用单件模式来实现。
单件可以看成是一个全局变量并且它会带来很多头疼的问题和奇怪的行为在多线程应用程序中。如果我们使用单模式,那么我们要进行适当的调整。
public class Earth {
private static Earth _instance = new Earth();
private Earth() { }
public static Earth GetInstance() { return _instance; }
}
我们拥有一个私有的构造器因此用户只能通过静态的GetInstance()方法来获取一个Earth实例。这是一个比较经典的线程安全实现,因为CLR会去创建安全的静态变量。这也是c#中我发现的最优雅的单件实现模式。
总结
1.不要留下打开的资源!明确关闭所有连接和清理所有非托管资源。一个通用的规则在using块内使用非托管资源。
2.不要过度的使用引用。当我们的对象活着,那么所有相关的引用对象将不会被回收。当我们操作了引用类的一些属性后,我们需要明确的将引用变量设置为null。以便垃圾回收器回收这些对象。
3.使用终结器(finalizer)使工作更容易,但是是在必须的情况下。终结器(finalizer)需要花费垃圾回收器的昂贵的代价,所以必须在必要的时候使用它。一个更好的方案是使用IDisposible 接口来取代终结器(finalizer)。这样做会使垃圾回收器工作的更有效率。
4.将对象和它们的孩子保持在一起。这样使得垃圾回收器更容易去产生大块内存而不用去收集托管堆上的每一个零散的内存。因此当我们声明一个对象由多个其他对象组合成的时候,我们应该显示的将它们安排的紧密一些。