2016-03-02 102 views
1

我有一個ApplicationContextForm。各種異步通信和定時線程可能有時需要重新啓動應用程序,但是在我可以重新啓動之前,我必須手動處理MyApplicationContext,當新進程啓動時需要釋放將需要的資源立即Form.Dispose()方法內的安全調用

在這種情況下,似乎只是撥打Application.Restart()不會足夠快地處理資源。

在對MyApplicationContext.Dispose()的調用中,隨後調用base.Dispose(disposing)最終調用Form.Dispose()方法,並且因爲這可能來自於各種線程,所以我看到發生了跨線程操作異常。

/// MyApplicationContext.requestRestart() 
private void requestRestart() 
{ 
    this.Dispose(); // dispose of applicationcontext 
    Application.Restart(); 
} 

導致...

/// MyApplicationContext.Dispose(bool) 
protected override void Dispose(bool disposing) 
{ 
    /// dispose stuff 
    base.Dispose(disposing); 
} 

導致...

/// MainForm.Dispose(bool) 
protected override void Dispose(bool disposing) 
{ 
    /// dispose stuff 
    base.Dispose(disposing); 
} 

可以從任何線程調用。

是否可以在Form這樣的UI線程上重寫Invoke覆蓋的配置處理程序?

protected override void Dispose(bool disposing) 
{ 
    if (this.InvokeRequired) 
    { 
     this.Invoke(new Action(() => Dispose(disposing))); 
    } 
    else 
    { 
     if (disposing && (components != null)) 
     { 
      components.Dispose(); 
     } 
     base.Dispose(disposing); 
    } 
} 

回答

2

不,它不是安全地調用Invoke()Dispose()實現。

好的,現在我已經說過了:當然,你可能會逃避它。但這是一個非常糟糕的主意,它確實可能導致真正的問題。

該方法應該是簡單。它不應該訪問託管對象,並且應該快速完成。調用Invoke()可能會導致不確定的延遲,這取決於程序中還在發生什麼,甚至是徹底的死鎖。

除此之外,Dispose()方法中的一條重要規則是不訪問託管對象。至少,您的實現首先需要檢查disposing,以確保您在從終結器中調用時不會嘗試調用Invoke()。此外,處理當前用於調用Invoke()方法的對象當然也不是一個好主意。如果您確實必須使用跨線程調用,則正確的方法是爲當前對象檢索SynchronizationContext,並使用它而不是調用Control.Invoke()


但的確,這裏要做的正確的事情是修復重啓邏輯,以便整個處理操作發生在擁有正在處理的對象的UI線程中。你的對象不是應該將執行轉移到正確線程的責任;代碼使用這些對象應該代替。在涉及類的代碼時,您應該已經確保代碼在擁有該對象的UI線程中執行。

+0

事實上,我是*隨着它離開了。我認爲將requestRestart方法移動到Form類是很容易的,例如,如果需要跨線程調用,它將在調用堆棧的底部完成。但在這種情況下,無法保證啓動重新啓動請求的線程將成爲「Form」UI線程 - 例如,網絡IO到達時。 – khargoosh

+0

只要確保在實際_handling_重新啓動請求之前的清除操作之前確保已移至UI線程,就可以在任何地方啓動重新啓動請求。即使在那裏,你可能會發現使用'SynchronizationContext'而不是'Form'實例是有用/可取的,因爲大概'Form'實例將被作爲清理的一部分來處理(即你仍然有調用'在Invoke()方法返回之前將對象放在一個對象上調用()')。 –

+0

我接受了您的好建議,並使用主窗體中的'SynchronizationContext'。謝謝彼得。我現在感覺好多了。 – khargoosh

相關問題