Design Pattern : Singleton
Definition :
「Ensure
a class has only one instance and provide a global point of access to it」
Singleton通常使用於只能建立某類別的單一個物件,例如 取號碼機(某段時間的號碼唯一)、戶政事務所發身分證(身分字號唯一), 目的是確保不會有多人同時建立或是同時修改這個物件。
我在先前有寫了一篇「Singleton in MutiThread AP」
裡面有三種利用Singleton建立取號機的方式,但是其實文章中”取號”的動作卻不是Thread-Safe; 雖然程式碼中可以確保Multi-thread中是取得同一個取號機,但是取號的動作其實還必須lock才能確保取號的動作是Thread-Safe,否則還是有可能取道同樣的號碼。 (文章就不修改,留下不經一事不長一智的紀錄 XD)
底下是利用Singleton取得每台汽車的CarUid, 可以把它想成車牌號碼,必須是唯一的。
1. Singleton Class :
=> 使用Thread-safe, lazy singleton
=> 注意在實際取得CarUid時,有使用lock以確保Thread-safe
public class CarUidSingleton
{
/// Car's Unique id
private string Uid = "000000";
private static readonly object block = new object();
public static CarUidSingleton GetInstance
{
get
{ return InnerClass.instance; }
}
public String GetUid()
{
lock(block)
{
int _uid = 0;
if (int.TryParse(this.Uid, out _uid))
{
_uid++;
this.Uid = _uid.ToString().PadLeft(6, '0');
}
else
throw new Exception("The Uid is not correct!");
return this.Uid;
}
}
private class InnerClass
{
static InnerClass() { }
internal static readonly CarUidSingleton instance = new CarUidSingleton();
}
}
=> 使用Thread-safe, lazy singleton
=> 注意在實際取得CarUid時,有使用lock以確保Thread-safe
public class CarUidSingleton
{
/// Car's Unique id
private string Uid = "000000";
private static readonly object block = new object();
public static CarUidSingleton GetInstance
{
get
{ return InnerClass.instance; }
}
public String GetUid()
{
lock(block)
{
int _uid = 0;
if (int.TryParse(this.Uid, out _uid))
{
_uid++;
this.Uid = _uid.ToString().PadLeft(6, '0');
}
else
throw new Exception("The Uid is not correct!");
return this.Uid;
}
}
private class InnerClass
{
static InnerClass() { }
internal static readonly CarUidSingleton instance = new CarUidSingleton();
}
}
2.
主程式:
=> 剛好拿Async-Awiat和Task兩種方式來實做Multi-thread的測試程式…
List<ICar> carList = new List<ICar>();
for(int i=0; i<5; i++)
{
carList.Add(new Focus());
carList.Add(new Civic());
carList.Add(new Fit());
carList.Add(new V60());
carList.Add(new Accord());
};
Parallel.ForEach(carList, car =>
{
#region Async-Await
GetCarUidInfo_UseAsyncAwait(car); ///請參考3.
#endregion
#region Task
GetCarUidInfo_UseTask(car); ///請參考4.
#endregion
}
);
=> 剛好拿Async-Awiat和Task兩種方式來實做Multi-thread的測試程式…
List<ICar> carList = new List<ICar>();
for(int i=0; i<5; i++)
{
carList.Add(new Focus());
carList.Add(new Civic());
carList.Add(new Fit());
carList.Add(new V60());
carList.Add(new Accord());
};
Parallel.ForEach(carList, car =>
{
#region Async-Await
GetCarUidInfo_UseAsyncAwait(car); ///請參考3.
#endregion
#region Task
GetCarUidInfo_UseTask(car); ///請參考4.
#endregion
}
);
3.
Async-Await:
private async static void GetCarUidInfo_UseAsyncAwait(ICar car)
{
String uid = await GetAsyncCarUid();
Console.WriteLine( String.Format("{0}({1})", car.Name, uid));
}
private async static Task<String> GetAsyncCarUid()
{
Thread.Sleep(1500);
return CarUidSingleton.GetInstance.GetUid();
}
private async static void GetCarUidInfo_UseAsyncAwait(ICar car)
{
String uid = await GetAsyncCarUid();
Console.WriteLine( String.Format("{0}({1})", car.Name, uid));
}
private async static Task<String> GetAsyncCarUid()
{
Thread.Sleep(1500);
return CarUidSingleton.GetInstance.GetUid();
}
4. Task:
private static void GetCarUidInfo_UseTask(ICar car)
{
Task<String> _task = new Task<String>(
() => CarUidSingleton.GetInstance.GetUid()
);
_task.Start();
_task.ContinueWith(TaskEnded, car);
}
private static void TaskEnded(Task<String> task, object car)
{
String uid = task.Result;
Console.WriteLine(
String.Format("{0}({1})", (car as ICar).Name, uid));
}
private static void GetCarUidInfo_UseTask(ICar car)
{
Task<String> _task = new Task<String>(
() => CarUidSingleton.GetInstance.GetUid()
);
_task.Start();
_task.ContinueWith(TaskEnded, car);
}
private static void TaskEnded(Task<String> task, object car)
{
String uid = task.Result;
Console.WriteLine(
String.Format("{0}({1})", (car as ICar).Name, uid));
}
5. 結果:
Fit(000001)
Accord(000002)
Focus(000003)
Civic(000004)
V60(000005)
Fit(000006)
Accord(000007)
Accord(000008)
…
Focus(000024)
Civic(000025)
Fit(000001)
Accord(000002)
Focus(000003)
Civic(000004)
V60(000005)
Fit(000006)
Accord(000007)
Accord(000008)
…
Focus(000024)
Civic(000025)