在 Windows Phone 7.1、8.0 的框架下,應用程式想在背景進行某些邏輯動作時只有撰寫 BackgroundAgent 一途,而 BackgroundAgent 不論是 AudioPlayerAgent 還是 ScheduledTaskAgent 等 Class 都有個特性,它們和應用程式本身並不屬於同一個 Process 區間,所以像 Singleton 等這類用來管理單一物件的手法是沒辦法使用的,會造成 Application 本身的 Process 有一個 singleton object、Agent 本身也有一個 singleton object。
因為這種狀況,在控管資源如檔案、設定值、資料表的同步問題就必須用到像 Mutex 這類可跨 Process 的元件,以我們專案中 AudioPlayerAgent 的這段程式碼為例
public void LogEvent(String eventId)
{
SqliteConnection DBConn = null;
try
{
AppService.Instance.mDBMutex.WaitOne();
try
{
DBConn = new SqliteConnection(Constants.SQLITE_DB_PATH);
if (DBConn != null)
{
DBConn.Open();
// LogEvent(eventId, DBConn);
}
}
catch (Exception e)
{
KKLogger.Instance.LogE(e);
}
}
finally
{
if (DBConn != null && DBConn.State == System.Data.ConnectionState.Open)
{
DBConn.Close();
}
AppService.Instance.mDBMutex.ReleaseMutex();
}
}
如果 Agent 正在執行這個 function 的途中,應用程式呼叫了 BackgroundAudioPlayer.Instance.Close(); 這類會釋放 Agent 資源的方法時,Agent 是會被立即 Release 的,並不會等待執行中的 Thread 結束,也就是上面這個 function 的 finally 不會被執行到的機率其實不低,也就發生 mDBMutex 沒被 Release 的情況。
在這種情況下,接下來所有的 mDBMutex 操作都會失敗,又由於 Mutex 對於系統來說是全域的,所以如果在 Application 啟動時就去操作同一個 Mutex 即會造成應用程式連開都開不起來,必須重新開機才有效。
所以這段看似嚴格包了很多層保護機制的寫法也是不夠的,在應用程式要將 Agent 釋放時,理想的方法是等待 Agent 的 Thread 執行結束時再釋放,比較容易的方法是使用旗標,讓 Agent 在確定釋放 Mutex 後主動檢查是否有人呼叫 Close 方法,由 Agent 自已在適當的時機關閉自己。
沒有留言:
張貼留言