2013年3月29日 星期五

[C#] Garbage Collection (3)

C# : Garbage Collection ()

  
續前文,拜讀前輩文章後,提到我們須習慣將類別的Dispose方法定義為virtual,以便讓繼承此類別的子類別可以在他們的Dispose方法呼叫父類別的Dispose方法,確實做到回收父子類別用到的記憶體。

所以我們將上篇文章 [C#] Garbage Collection(2) CSampleClass Dispose方法宣告為virtual :

/// <summary>
/// Virtual Dispose
/// </summary>
public virtual void Dispose()
{
   DoDispseStep();
}



然後寫一個子類別 CChildClass,繼承CSampleClass
並在自己的Dispose方法中,呼叫父類別的Dispose方法: base.Dispose();

public class CChildClass : CSampleClass, IDisposable
{
        public Dictionary<String, String> Pets;
        private bool DisposedFlag = false;

        /// <summary>
        /// 建構
        /// </summary>
        public CChildClass()
        {
            Pets = new Dictionary<string, string>();
        }

        /// <summary>
        /// Dispose
        /// </summary>
        public  virtual void Dispose()
        {
            if (DisposedFlag == false)
            {
                Console.WriteLine("移除 Pets物件 {0} 筆資料", Pets.Count);
                Pets = null;

                //引用父類別的GC方法
                base.Dispose();

                DisposedFlag = true;
            }
        }
}


主程式就不寫了,當宣告一個CChildClass的物件後,並dispose他的時候,顯示以下訊息


移除 Pets物件 1 筆資料       → 源自CChildClassDispose方法
移除 ownedCar物件 2 筆資料   → 源自CSampleClassDispose方法





2013年3月28日 星期四

[C#] Garbage Collection (2)

C# : Garbage Collection (2)

上篇文章
有提到Dispose,拜讀幾篇前輩的文章後,發現Dispose也是有學問的,
第一件事當然是要記得實作Dispose方法,第二件事情是要讓Dispose方法可以被重複引用。

Example
建立一個CSampleClass類別如下

class CSampleClass : IDisposable
{
        public String Name { get; set; }
        public Dictionary<String, String> ownedCar;
       
        /// <summary>
        /// 建構
        /// </summary>
        public CSampleClass()
        {
            ownedCar = new Dictionary<string, string>();
        }

        /// <summary>
        /// 解構
        /// </summary>
        ~CSampleClass()
        {
            DoDispseStep();
        }
        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            DoDispseStep();
        }
        /// <summary>
        /// Dispose 的步驟
        /// </summary>
        private void DoDispseStep()
        {
            Name = "";
             Console.WriteLine("移除 ownedCar {0} 筆資料", ownedCar.Count);
             ownedCar = null;
        }
}



主程式

using (CSampleClass sample = new CSampleClass())
{
     sample.Name = "JB";
     sample.ownedCar.Add("FOCUS 1.6", "2012" );
     sample.ownedCar.Add("MAZDA 1.6", "2005" );

var cars = from x in sample.ownedCar.AsQueryable()
select new { Name = x.Key , Year = x.Value };
foreach(var item in cars){
Console.Write("車種 {0},年份 {1}\n", item.Name, item.Year);
}
}



看起來不但實作了Dispose方法,連解構方法我們都加入了。
眼尖的朋友應該已經看到問題了,在DoDispseStep()方法中,這一行程式碼會有問題
Console.WriteLine("
移除 ownedCar {0} 筆資料", ownedCar.Count);

實際執行程式後,USING此物件結束後會去Call Dispose()方法,此時Dictionary物件ownedCar仍有值,所以這一行還不會有問題。 但是關閉程式時,GC機制會再去Call物件的Finalizer(解構方法),這時候ownedCar時候已經沒有參考任何值了,所以就會Error

所以要做到「讓Dispose方法可以被重複引用」,可以

ü   判斷物件是否需要Dispose

例如上面的DoDispseStep程式碼可改為

if (ownedCar != null && ownedCar.Count > 0)
{

   Console.WriteLine("
移除 ownedCar {0} 筆資料", ownedCar.Count);
   ownedCar = null;
}




ü   設定一個Dispose Flag判斷是否已經做過Dispose

在類別裡面放一個是否做過Dispose的註記:
private bool DisposedFlag = false;



DoDispseStep
程式碼改為先判斷此註記
if (DisposedFlag == false)
{

   Name = "";
   Console.WriteLine("
移除 ownedCar {0} 筆資料", ownedCar.Count);
   ownedCar = null;
   DisposedFlag = true;
}


ü   Dispose後加上 GC.SupperessFinalize(物件),告訴CLR此物件已正確清除,不必再到Finalizer(解構)

public void Dispose()
{
DoDispseStep();
GC.SuppressFinalize(this);
}


關於GC.SupperessFinalize的用法時機可參考此文 :




2013年3月21日 星期四

負載測試流程 (with Visual Studio 2010 SP1 ) (二)




負載測試流程 (with Visual Studio 2010 SP1 ) ()
作者:JB


一、 負載測試也可以搭配已寫好的單元測試,來測試多工的系統(如WCF, Web api … etc
1.          先寫好一個單元測試
2.          在測試專案加入一個負載測試
3.          在測試混合中,選擇步驟一的單元測試



二、 如果單元測試裡面的內容,有包含initial(或結束)資料或設定的部分,建議把她們拿到其他函式。 然後在『測試混合』中的【初始化和結束測試】加上這部分,避免影響負載測試的結果
三、 如果是要測試Concurrent (平行執行) Sequential(依序執行) 的程式,請參考下表:


測試混合類型
回合設定
平行執行Concurrent
按虛擬使用者人數
(
使用者人數:即要同時執行的Thread數目)

測試反覆項目:
即要同時執行的案例數目
依序執行Sequential
按虛擬使用者人數
使用者人數:1
(只開一條Thread)
測試反覆項目:
總執行的案例數目

範例一: 平行執行某一Test Method 兩個不同案例
a.
測試混合類型:選擇 「按虛擬使用者人數」,使用者人數設定為 2
b. 回合設定:「測試反覆項目」設定為 2





範例二: 依序執行某一Test Method 兩個不同案例
a. 測試混合類型:選擇 「按虛擬使用者人數」,使用者人數設定為 1
b. 回合設定:「測試反覆項目」設定為 2




四、 如何在負載測試確保每跑一次程式(不管是Concurrent/Sequential),都是跑不同的資料?
1.          建立一個放資料的物件集合X
2.          建立一個Initial測試資料到X的函式 Init_TestData()
3.          建立要LOCK的靜態object 和 目前拜訪到的測試資料index靜態變數 :
=>
在單元測試裡面的建構函式呼叫
Init_TestData() Initial測試資料時,要Lock,避免同時做多次Intial 
=>
取得測試資料時,要Lock並在取得後將測試資料index 1,下次就會抓到下一筆資料。
4.           程式碼範例:
/// Contactless加值POS案例集合
private static List<資料物件> X= new List<資料物件>();
 //Lock Intial建構子
private static object lockObj_Init = new object();
//Lock 測試案例index 的物件
private static object lockObj = new object();
//
測試案例index For負載測試
private static  int TestIndex = 0;

/// <summary>

/// 初始化測試資料
/// </summary>
private void Init_TestData ()
{
   //設定測試案例給 X …
}

/// <summary>
///
建構函式
/// </summary>
public UnitTest()
{
  lock (lockObj_Init)
  {
     if (X.Count == 0)  {  this.Init_TestData();  }
            }
}

/// <summary>
/// Test Method
/// </summary>
 [TestMethod]
public void MyTestMethod()
{
    //
使用lock TestIndex 確保每次取得不同的測試資料
                lock ( lockObj )
                {
                    _testCase = X [TestIndex];
                    TestIndex++;
                 }
                 //Test Code…
            
}


結束