我有一個類型的控件,包含一個GridView和一些實用程序按鈕。該控件在我的應用程序中隨處可見。它通過代表異步填充:試圖避免例外時調用回UI線程
protected virtual void PopulateGridView()
{
if (isPopulating) return;
//a delegate given to the control by its parent form
if (GetterMethod != null)
{
isPopulating = true;
/*unimportant UI fluff here*/
//some controls are fast enough to not have to mess with threading
if(PopulateSynchronously)
{
PopulateWithGetterMethod();
InitializeGridView();
}
else //most aren't
{
Action asyncMethod = PopulateWithGetterMethod;
asyncMethod.BeginInvoke(
ar => Invoke((MethodInvoker)InitializeGridView)), null);
}
}
}
private void PopulateWithGetterMethod()
{
//a list of whetever the control is displaying;
//the control ancestor and this collection are generic.
RetrievedInformation = GetterMethod();
}
protected virtual void InitializeGridView()
{
//use RetrievedInformation to repopulate the GridView;
//implementation not important, except it touches UI elements,
//so it needs to be called from the worker thread using Invoke.
}
對於長時間運行的查詢,有時用戶會感到不耐煩並關閉窗口。或者,當其中一個控件基於定時器自動刷新時,用戶會偶然關閉一個窗口。當發生這種情況並且查詢DID完成時,回調委託中的Invoke調用將因InvalidOperationException失敗,因爲該控件沒有窗口句柄。
爲了解決這個問題,我試圖使用內置IsHandleCreated屬性:
...
else
{
Action asyncMethod = PopulateWithGetterMethod;
asyncMethod.BeginInvoke(
ar => { if(IsHandleCreated)
Invoke((MethodInvoker)InitializeGridView));
}, null);
}
然而,異常仍然發生,只是不經常。我設法重現它,並發現Invoke調用仍然發生,即使IsHandleCreated上的監視顯示爲false。我的猜測是線程在檢查和Invoke調用之間被搶佔,就像你在提升它之前檢查一個事件委託爲null一樣。
我還是有選擇,我想,但我想知道什麼是最好的是:
- 檢查不僅IsHandleCreated,但處置,以確保控制真的是活得很好,而不僅僅是即將被銷燬。
- 在進行檢查之前執行一個Thread.Yield(),以便操作系統有機會在檢查句柄之前執行任何窗口管理。
- 將try調用封裝在try/catch中,以抑制任何InvalidOperationException異常,或者至少有一個報告缺少窗口句柄。老實說,在這種情況下,我不在乎GridView不能更新;用戶關閉了窗戶,顯然他們不在乎。讓線程安靜地死去,而不會取消整個應用程序。
第三個選項看起來像是一個cop-out;必須有一個更清潔的方式來處理它。但是,我不確定其他兩個中的任何一個都是100%修復。
編輯:檢查處置和IsDisposed也不工作;我在if條件塊中拋出了一個異常,條件爲「IsHandleCreated & &!Disposing & &!IsDisposed」,其中第一個和最後一個節點在觀察時爲假。目前,我正在使用消息「在調用窗口句柄之前無法在控件上調用Invoke或BeginInvoke」來捕獲所有異常,這正是我希望不要這樣做的。
優雅,我大概可以找出一些會沿着這些線路工作的東西。但是,我的情況並不那麼簡單,異步代碼封裝在UserControl中,而不是Form。在一個窗口中可以有幾個,所以解決方案必須確保所有線程都已執行。最後,這個錯誤的頭號原因是用戶要麼厭倦了,要麼意識到他們犯了一個錯誤,然後點擊X;在用戶想要它離開後保持窗口不是理想的用戶體驗。這些都不是不可逾越的。 – KeithS 2011-04-21 17:39:28
我們遇到了同樣的問題,當我們從後臺線程顯示錯誤消息框時,Keith正在描述併吞下異常。沒有吞沒例外,真的沒辦法解決這個問題。除此之外,Windows句柄的處理順序不一定是它們的創建順序,因此您不能依賴邏輯流程來擺脫這種情況。即使在你的例子中,你正在使用mCompleted和mClosePending進行比賽。 – 2011-04-21 19:03:50
不,這些變量只能在UI線程上訪問過。那不能比賽。 – 2011-04-21 19:19:36