謹防靜態變量
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.將對象和它們的孩子保持在一起。這樣使得垃圾回收器更容易去產生大塊內存而不用去收集托管堆上的每一個零散的內存。因此當我們聲明一個對象由多個其他對象組合成的時候,我們應該顯示的將它們安排的緊密一些。