心得 如何优雅地跨线程修改主线程窗口控件
編程環(huán)境要求:VS2008/FX2.0
眾所周知,從VS2005/FX2.0起,在多線程環(huán)境下是不允許跨線程修改主線程上窗口控件的。
例如:
?
private?void?button1_Click(object?sender,?EventArgs?e){
????Thread?t?=?new?Thread(new?ThreadStart(CrossThreadCall));
????t.Start();
}
public?void?CrossThreadCall()
{
????Text?=?"test";
}
將直接導(dǎo)致異常:
未處理 System.InvalidOperationException
? Message="線程間操作無效: 從不是創(chuàng)建控件“Form1”的線程訪問它。"
? Source="System.Windows.Forms"
? StackTrace:
?????? 在 System.Windows.Forms.Control.get_Handle()
?????? 在 System.Windows.Forms.Control.set_WindowText(String value)
?????? 在 System.Windows.Forms.Form.set_WindowText(String value)
?????? 在 System.Windows.Forms.Control.set_Text(String value)
?????? 在 System.Windows.Forms.Form.set_Text(String value)
?????? 在 delegate_test1.Form1.CrossThreadCall() 位置 f:\app\delegate_test1\delegate_test1\Form1.cs:行號 26
?????? 在 System.Threading.ThreadHelper.ThreadStart_Context(Object state)
?????? 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
?????? 在 System.Threading.ThreadHelper.ThreadStart()
通用的解決方法是使用Control.Invoke方法來調(diào)用一個(gè)Delegate,從而安全地跨線程調(diào)用。
例如:
?
public?void?CrossThreadCall(){
????Invoke(new?void_d(CrossThreadCall_Local));
}
void?CrossThreadCall_Local()
{
????Text?=?"test";
}
public?delegate?void?void_d();
但是這樣的缺點(diǎn)是要不得不為每個(gè)調(diào)用編寫一個(gè)Invoke跳板,還要額外聲明一個(gè)委托類型,實(shí)在不怎么優(yōu)雅。
于是我們想到用匿名函數(shù)來寫。我的第一反應(yīng)是:
?
Invoke(delegate?{?Text?=?"test";?});
可惜不行。編譯壓根就沒通過,寫著:
無法將 匿名方法 轉(zhuǎn)換為類型“System.Delegate”,因?yàn)樗皇俏蓄愋?br />
無語,delegate竟然不是委托類型?
等我把Google翻了一遍后,找到了答案。
The problem the user is seeing is that the Thread ctor accepts a specific delegate -- the ThreadStart delegate.? The C# compiler will check and make sure your anonymous method matches the signature of the ThreadStart delegate and, if so, produces the proper code under-the-covers to create the ThreadStart delegate for you.
But Control.Invoke is typed as accepting a "Delegate".? This means it can accept any delegate-derived type.? The example above shows an anonymous method that has a void return type and takes no parameters.? It's possible to have a number of delegate-derived types that match that signature (such as MethodInvoker and ThreadStart -- just as an example).? Which specific delegate should the C# compiler use?? There's no way for it to infer the exact delegate type so the compiler complains with an error.
也就是說,對于Thread.ctor()來說,由于接受的是一個(gè)ThreadStart委托,編譯器便可以將匿名函數(shù)與ThreadStart委托類型匹配,最后能夠正確編譯。
而對于Control.Invoke()來說,任何的代理類型都是可接受的,也就是說ThreadStart和MethodInvoker都是可以接受的類型。這樣編譯器反而不知道應(yīng)該用哪個(gè)代理去匹配匿名函數(shù)了,導(dǎo)致了編譯錯(cuò)誤的發(fā)生。
知道了原因,問題就很容易解決了。我們只需要加上MethodInvoker這個(gè)wrapper就能使用匿名函數(shù)了。
或者更簡單地,用Lambda表達(dá)式來解決問題:
希望本文能夠幫助有同樣困惑的朋友。
轉(zhuǎn)載于:https://www.cnblogs.com/msg7086/articles/1266096.html
總結(jié)
以上是生活随笔為你收集整理的心得 如何优雅地跨线程修改主线程窗口控件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。