2012-02-03 53 views
1
public void RefreshData() 
{ 
    // this is called on UI thread 

    List<ds> dataSource; 
    GetDsDelegate caller = GetDs; 
    caller.BeginInvoke(out dataSource, RefreshCallback, null); 
} 

private void RefreshCallback(IAsyncResult ar) 
{ 
    // this is called on worker thread 

    try 
    { 
     var result = (AsyncResult)ar; 
     var caller = (GetDsDelegate)result.AsyncDelegate; 

     List<ds> dataSource; 
     var success = caller.EndInvoke(out dataSource, ar); 

     if (success) 
     { 
      BeginInvoke(new Action<List<ds>>(SetGridDataSource), dataSource); 
     } 
    } 
    catch 
    { 
     // NOTE: It's possible for this form to close after RefreshData is called 
     // but before GetDs returns thus the SetGridDataSource method no longer exists. 
     // Not catching this error causes the entire application to terminate. 
    } 

private void SetGridDataSource(List<ds> dataSource) 
{ 
    // this is called on UI thread 
    dataGrid.DataSource = dataSource; 
} 

RefreshData,RefreshCallback和SetGridDataSource都是windows Form類的所有方法。調用RefreshData使用GetDsDelegate委託調用外部方法GetDs。當GetDs完成時,它調用RefreshCallback(現在在一個單獨的線程上)。最後,調用SetGridDataSource來完成更新。如何處理丟失的調用方法

所有這些工作正常,除非GetDs被延遲並且窗體關閉。然後,當GetDs完成並調用RefreshCallback時,SetGridDataSource不再存在。

除了顯示的try/catch塊之外,還有更好的方法來處理這種情況嗎?我寧願防止錯誤,而不是忽略它。有更好的模式可供使用嗎?

編輯

我看着那個錯誤,很明顯改變if (success)if (success && IsHandleCreated),以防止它,但它仍然好像我做錯了什麼,或者至少尷尬。我也可以用Invoke替換第二個BeginInvoke,所以EndInvoke是不必要的。我喜歡將邏輯從表單移開的想法,但我不明白結果會如何改變。我認爲BackgroundWorker也會有同樣的問題;作爲回調不再可訪問。我想可以提出一個事件的結果,但這似乎有點抽象。你能否詳細說明一下或者提供一個例子。

+1

這是一個無法解決的競爭條件,你不能允許表單關閉,直到所有工作線程完成運行。使用BackgroundWorker可以更輕鬆地完成此任務。 – 2012-02-03 02:44:48

回答

0

該方法確實存在。表單被關閉的事實不會改變類或其方法。如果您發佈了確切的例外情況,它可以幫助某人提供更準確的幫助。

我想你有一個ObjectDisposedException當你試圖在窗體上或關閉它後的一個控件之後做一些事情。

如果是這種情況,您只需增強邏輯並在調用SetGridDataSource之前檢查表單是否已關閉或放置。

也就是說,你的設計似乎存在一些問題。

  1. 您從線程池調用SetGridDataSource,但它可能應該從UI線程調用。
  2. Your'e不正確鏈接異步調用。具體而言,您不會在第二個BeginInvoke上撥打EndInvoke
  3. 一個可以幫助您獲得線程權限的好方法是使用類似BackgroundWorker而不是鏈接BeginInvoke的調用。
  4. 考慮將業務邏輯(包括線程和異步邏輯)從UI層和表單移開。
  5. 在沒有異常類型的情況下執行catch是一個非常糟糕的主意,並且通過在不重新拋出的情況下忽略它而處理拋出的異常。

順便說一句,而不是做這樣的:

BeginInvoke(new Action<List<ds>>(SetGridDataSource), dataSource); 

你能做到以下幾點,我認爲這是更具可讀性:

BeginInvoke(SetGridDataSource, dataSource); 
+0

.NET 4編譯器是否支持最後的推理示例?我很確定它在.NET 3.5中不起作用。 – Groo 2012-02-03 01:02:52

+0

感謝您的回覆。調用SetGridDataSource產生一個「Invoke或BeginInvoke不能在控件上調用,直到窗口句柄被創建。」例外。鑑於表格已關閉,這對我來說很有意義。 – rastro 2012-02-03 01:38:44