現實情況是,對於Invoke和朋友,您無法完全防止對已處置組件的調用,然後由於缺少句柄而導致InvalidOperationException。我還沒有真正看到過答案,就像下面的答案一樣,在任何解決先前測試或使用鎖定語義無法完全解決的問題性問題的線程中。
這裏是正常的 '正確' 的成語:
// the event handler. in this case preped for cross thread calls
void OnEventMyUpdate(object sender, MyUpdateEventArgs e)
{
if (!this.IsHandleCreated) return; // ignore events if we arn't ready, and for
// invoke if cant listen to msg queue anyway
if (InvokeRequired)
Invoke(new MyUpdateCallback(this.MyUpdate), e.MyData);
else
this.MyUpdate(e.MyData);
}
// the update function
void MyUpdate(Object myData)
{
...
}
的fundemental問題:
在使用Invoke設施中使用的窗口消息隊列,這在放置一個消息排隊等待,或者像發佈或發送消息一樣完全消除交叉線程調用。如果在Invoke消息之前有一條消息會使組件及其窗口句柄無效,或者在您嘗試執行任何檢查之後放置該消息,那麼您將會遇到不好的時間。
x thread -> PostMessage(WM_CLOSE); // put 'WM_CLOSE' in queue
y thread -> this.IsHandleCreated // yes we have a valid handle
y thread -> this.Invoke(); // put 'Invoke' in queue
ui thread -> this.Destroy(); // Close processed, handle gone
y thread -> throw Invalid....() // 'Send' comes back, thrown on calling thread y
有知道該控件是要去掉自己fromthe隊列中沒有真正的方法,並沒有什麼合理的可以做,以「撤銷」的調用。不管你做了多少次檢查或你製造了額外的鎖,你都不能阻止其他人發佈類似關閉的東西,或停用。有很多senarios會發生這種情況。
A液:
首先要知道的是,調用是要失敗的不是如何(IsHandleCreated)檢查將忽略該事件,沒有什麼不同。如果目標是保護非UI線程上的調用者,則需要處理該異常,並將其視爲任何其他未成功的調用(以防止應用程序崩潰或執行任何操作),除非要重寫/重擲調用設施,美中不足的是你知道的唯一途徑。
// the event handler. in this case preped for cross thread calls
void OnEventMyWhatever(object sender, MyUpdateEventArgs e)
{
if (!this.IsHandleCreated) return;
if (InvokeRequired)
{
try
{
Invoke(new MyUpdateCallback(this.MyUpdate), e.MyData);
}
catch (InvalidOperationException ex) // pump died before we were processed
{
if (this.IsHandleCreated) throw; // not the droids we are looking for
}
}
else
{
this.MyUpdate(e.MyData);
}
}
// the update function
void MyUpdate(Object myData)
{
...
}
異常過濾可以定製,以滿足任何的需求是什麼。其良好的知道工作線程往往不具備所有輕鬆的外異常處理並在大多數應用程序中記錄UI線程,因此您可能希望只是在工作端吞噬任何異常,或者記錄並重新拋出所有這些異常。對於很多工作線程中未捕獲的異常,意味着應用程序將崩潰。
爲什麼它處在第一位? – RvdK 2009-12-09 15:51:32
@PoweRoy:我發信號線程退出控件的Dispose方法。我知道這不是最好的做法,但我找不到一個更好的地方來指示線程退出。 – 2009-12-09 16:06:58