當一個地雷踩第二次的時候,就是以文字記錄下來的時候。
重構是程式設計師每天的功課,我每一段時間都會針對code進行重構,整理程式也整理思緒,
最近重構看到了App.xaml中有著這段程式碼,在OnStartup裡。
private static Semaphore singleInstanceWatcher;
..........
protected override void OnStartup(StartupEventArgs e)
{
bool createdNew = false;
base.OnStartup(e);
singleInstanceWatcher = new Semaphore(
0, // Initial count.
1, // Maximum count.
Assembly.GetExecutingAssembly().GetName().Name,
out createdNew);
if (!createdNew)
{
MessageQueueManager.PostMessage("instance already exists, wake up it");
Process current = Process.GetCurrentProcess();
bool found = false;
foreach (Process process in
Process.GetProcessesByName(current.ProcessName))
{
if (process.Id != current.Id)
{
found = true;
break;
}
}
if (!found)
{
singleInstanceWatcher.Release();
return;
}
// Terminate this process and gives the underlying operating
// system the specified exit code.
Environment.Exit(-2);
}
.................
}
程式碼的目的很簡單,使用Semaphore來進行判斷同一支程式是否已經在執行,雖然運作很正常,但這明顯是一個需要重構的程式碼,因為這個動作是一個單元,至少必須移動到函式中,保持OnStartup的乾淨,更好的手法是把這個函式移動到另一個類別中,保持整個App.xaml的乾淨,不過第一階段當然是區域移動,所以改成下面這樣。
private bool CheckSingleInstance()
{
var singleInstanceWatcher = new Semaphore(
0, // Initial count.
1, // Maximum count.
Assembly.GetExecutingAssembly().GetName().Name,
out var createdNew);
if (!createdNew)
{
MessageQueueManager.PostMessage("instance already exists, wake up it");
Process current = Process.GetCurrentProcess();
bool found = false;
foreach (Process process in
Process.GetProcessesByName(current.ProcessName))
{
if (process.Id != current.Id)
{
found = true;
break;
}
}
if (!found)
{
singleInstanceWatcher.Release();
return true;
}
// Terminate this process and gives the underlying operating
// system the specified exit code.
Environment.Exit(-2);
}
return false;
}
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if(CheckSingleInstance())
return;
.................
}
除了提取成函式,也把一些看起來不需要的變數改成區域宣告,但這個程式卻偶爾會不正常,常常無法偵測到上一個Instance而導致開啟兩個同樣的程式,一時間腦袋有點打結,我只是移動了函式而已啊。後來感覺好像似曾相識,以前似乎踩過同樣的雷。
原因在於Semaphore,當他是類別變數時,生命週期會跟著實體物件,這是App,所以會持續到應用程式結束,但CheckSingleInstance是區域函式,singleInstanceWatcher會被GC收走,此時使用Semaphore來判斷Single Instance的手法就會失敗,也因為GC啟動的時間不定,所以偶而正常。