如何对 Windows 窗体控件进行线程安全调用.docx
《如何对 Windows 窗体控件进行线程安全调用.docx》由会员分享,可在线阅读,更多相关《如何对 Windows 窗体控件进行线程安全调用.docx(22页珍藏版)》请在冰豆网上搜索。
如何对Windows窗体控件进行线程安全调用
使用多线程提高Windows窗体应用程序的性能时,必须注意以线程安全方式调用控件。
示例
访问Windows窗体控件本质上不是线程安全的。
如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。
还可能出现其他与线程相关的bug,包括争用情况和死锁。
确保以线程安全方式访问控件非常重要。
.NETFramework有助于在以非线程安全方式访问控件时检测到这一问题。
在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个InvalidOperationException,并显示以下消息:
“从不是创建控件控件名称的线程访问它。
”
此异常在调试期间和运行时的某些情况下可靠地发生。
强烈建议您在显示此错误信息时修复此问题。
在调试以.NETFramework2.0版之前的.NETFramework编写的应用程序时,可能会出现此异常。
注意:
可以通过将CheckForIllegalCrossThreadCalls属性的值设置为false来禁用此异常。
这会使控件以与在VisualStudio2003下相同的方式运行。
下面的代码示例演示如何从辅助线程以线程安全方式和非线程安全方式调用Windows窗体控件。
它演示一种以非线程安全方式设置TextBox控件的Text属性的方法,还演示两种以线程安全方式设置Text属性的方法。
VisualBasic
复制代码
ImportsSystem
ImportsSystem.ComponentModel
ImportsSystem.Threading
ImportsSystem.Windows.Forms
PublicClassForm1
InheritsForm
'Thisdelegateenablesasynchronouscallsforsetting
'thetextpropertyonaTextBoxcontrol.
DelegateSubSetTextCallback([text]AsString)
'Thisthreadisusedtodemonstrateboththread-safeand
'unsafewaystocallaWindowsFormscontrol.
PrivatedemoThreadAsThread=Nothing
'ThisBackgroundWorkerisusedtodemonstratethe
'preferredwayofperformingasynchronousoperations.
PrivateWithEventsbackgroundWorker1AsBackgroundWorker
PrivatetextBox1AsTextBox
PrivateWithEventssetTextUnsafeBtnAsButton
PrivateWithEventssetTextSafeBtnAsButton
PrivateWithEventssetTextBackgroundWorkerBtnAsButton
PrivatecomponentsAsSystem.ComponentModel.IContainer=Nothing
PublicSubNew()
InitializeComponent()
EndSub
ProtectedOverridesSubDispose(disposingAsBoolean)
IfdisposingAndAlso(componentsIsNotNothing)Then
components.Dispose()
EndIf
MyBase.Dispose(disposing)
EndSub
'Thiseventhandlercreatesathreadthatcallsa
'WindowsFormscontrolinanunsafeway.
PrivateSubsetTextUnsafeBtn_Click(_
ByValsenderAsObject,_
ByValeAsEventArgs)HandlessetTextUnsafeBtn.Click
Me.demoThread=NewThread(_
NewThreadStart(AddressOfMe.ThreadProcUnsafe))
Me.demoThread.Start()
EndSub
'Thismethodisexecutedontheworkerthreadandmakes
'anunsafecallontheTextBoxcontrol.
PrivateSubThreadProcUnsafe()
Me.textBox1.Text="Thistextwassetunsafely."
EndSub
'Thiseventhandlercreatesathreadthatcallsa
'WindowsFormscontrolinathread-safeway.
PrivateSubsetTextSafeBtn_Click(_
ByValsenderAsObject,_
ByValeAsEventArgs)HandlessetTextSafeBtn.Click
Me.demoThread=NewThread(_
NewThreadStart(AddressOfMe.ThreadProcSafe))
Me.demoThread.Start()
EndSub
'Thismethodisexecutedontheworkerthreadandmakes
'athread-safecallontheTextBoxcontrol.
PrivateSubThreadProcSafe()
Me.SetText("Thistextwassetsafely.")
EndSub
'Thismethoddemonstratesapatternformakingthread-safe
'callsonaWindowsFormscontrol.
'
'Ifthecallingthreadisdifferentfromthethreadthat
'createdtheTextBoxcontrol,thismethodcreatesa
'SetTextCallbackandcallsitselfasynchronouslyusingthe
'Invokemethod.
'
'Ifthecallingthreadisthesameasthethreadthatcreated
'theTextBoxcontrol,theTextpropertyissetdirectly.
PrivateSubSetText(ByVal[text]AsString)
'InvokeRequiredrequiredcomparesthethreadIDofthe
'callingthreadtothethreadIDofthecreatingthread.
'Ifthesethreadsaredifferent,itreturnstrue.
IfMe.textBox1.InvokeRequiredThen
DimdAsNewSetTextCallback(AddressOfSetText)
Me.Invoke(d,NewObject(){[text]})
Else
Me.textBox1.Text=[text]
EndIf
EndSub
'Thiseventhandlerstartstheform's
'BackgroundWorkerbycallingRunWorkerAsync.
'
'TheTextpropertyoftheTextBoxcontrolisset
'whentheBackgroundWorkerraisestheRunWorkerCompleted
'event.
PrivateSubsetTextBackgroundWorkerBtn_Click(_
ByValsenderAsObject,_
ByValeAsEventArgs)HandlessetTextBackgroundWorkerBtn.Click
Me.backgroundWorker1.RunWorkerAsync()
EndSub
'ThiseventhandlersetstheTextpropertyoftheTextBox
'control.Itiscalledonthethreadthatcreatedthe
'TextBoxcontrol,sothecallisthread-safe.
'
'BackgroundWorkeristhepreferredwaytoperformasynchronous
'operations.
PrivateSubbackgroundWorker1_RunWorkerCompleted(_
ByValsenderAsObject,_
ByValeAsRunWorkerCompletedEventArgs)_
HandlesbackgroundWorker1.RunWorkerCompleted
Me.textBox1.Text=_
"ThistextwassetsafelybyBackgroundWorker."
EndSub
#Region"WindowsFormDesignergeneratedcode"
PrivateSubInitializeComponent()
Me.textBox1=NewSystem.Windows.Forms.TextBox()
Me.setTextUnsafeBtn=NewSystem.Windows.Forms.Button()
Me.setTextSafeBtn=NewSystem.Windows.Forms.Button()
Me.setTextBackgroundWorkerBtn=NewSystem.Windows.Forms.Button()
Me.backgroundWorker1=NewSystem.ComponentModel.BackgroundWorker()
Me.SuspendLayout()
'
'textBox1
'
Me.textBox1.Location=NewSystem.Drawing.Point(12,12)
Me.textBox1.Name="textBox1"
Me.textBox1.Size=NewSystem.Drawing.Size(240,20)
Me.textBox1.TabIndex=0
'
'setTextUnsafeBtn
'
Me.setTextUnsafeBtn.Location=NewSystem.Drawing.Point(15,55)
Me.setTextUnsafeBtn.Name="setTextUnsafeBtn"
Me.setTextUnsafeBtn.TabIndex=1
Me.setTextUnsafeBtn.Text="UnsafeCall"
'
'setTextSafeBtn
'
Me.setTextSafeBtn.Location=NewSystem.Drawing.Point(96,55)
Me.setTextSafeBtn.Name="setTextSafeBtn"
Me.setTextSafeBtn.TabIndex=2
Me.setTextSafeBtn.Text="SafeCall"
'
'setTextBackgroundWorkerBtn
'
Me.setTextBackgroundWorkerBtn.Location=NewSystem.Drawing.Point(177,55)
Me.setTextBackgroundWorkerBtn.Name="setTextBackgroundWorkerBtn"
Me.setTextBackgroundWorkerBtn.TabIndex=3
Me.setTextBackgroundWorkerBtn.Text="SafeBWCall"
'
'backgroundWorker1
'
'
'Form1
'
Me.ClientSize=NewSystem.Drawing.Size(268,96)
Me.Controls.Add(setTextBackgroundWorkerBtn)
Me.Controls.Add(setTextSafeBtn)
Me.Controls.Add(setTextUnsafeBtn)
Me.Controls.Add(textBox1)
Me.Name="Form1"
Me.Text="Form1"
Me.ResumeLayout(False)
Me.PerformLayout()
EndSub'InitializeComponent
#EndRegion
_
SharedSubMain()
Application.EnableVisualStyles()
Application.Run(NewForm1())
EndSub
EndClass
C#
复制代码
usingSystem;
usingSystem.ComponentModel;
usingSystem.Threading;
usingSystem.Windows.Forms;
namespaceCrossThreadDemo
{
publicclassForm1:
Form
{
//Thisdelegateenablesasynchronouscallsforsetting
//thetextpropertyonaTextBoxcontrol.
delegatevoidSetTextCallback(stringtext);
//Thisthreadisusedtodemonstrateboththread-safeand
//unsafewaystocallaWindowsFormscontrol.
privateThreaddemoThread=null;
//ThisBackgroundWorkerisusedtodemonstratethe
//preferredwayofperformingasynchronousoperations.
privateBackgroundWorkerbackgroundWorker1;
privateTextBoxtextBox1;
privateButtonsetTextUnsafeBtn;
privateButtonsetTextSafeBtn;
privateButtonsetTextBackgroundWorkerBtn;
privateSystem.ComponentModel.IContainercomponents=null;
publicForm1()
{
InitializeComponent();
}
protectedoverridevoidDispose(booldisposing)
{
if(disposing&&(components!
=null))
{
components.Dispose();
}
base.Dispose(disposing);
}
//Thiseventhandlercreatesathreadthatcallsa
//WindowsFormscontrolinanunsafeway.
privatevoidsetTextUnsafeBtn_Click(
objectsender,
EventArgse)
{
this.demoThread=
newThread(newThreadStart(this.ThreadProcUnsafe));
this.demoThread.Start();
}
//Thismethodisexecutedontheworkerthreadandmakes
//anunsafecallontheTextBoxcontrol.
privatevoidThreadProcUnsafe()
{
this.textBox1.Text="Thistextwassetunsafely.";
}
//Thiseventhandlercreatesathreadthatcallsa
//WindowsFormscontrolinathread-safeway.
privatevoidsetTextSafeBtn_Click(
objectsender,
EventArgse)
{
this.demoThread=
newThread(newThreadStart(this.ThreadProcSafe));
this.demoThread.Start();
}
//Thismethodisexecutedontheworkerthreadandmakes
//athread-safecallontheTextBoxcontrol.
privatevoidThreadProcSafe()
{
this.SetText("Thistextwassetsafely.");
}
//Thismethoddemonstratesapatternformakingthread-safe
//callsonaWindowsFormscontrol.
//
//Ifthecallingthreadisdifferentfromthethreadthat
//createdtheTextBoxcontrol,thismethodcreatesa
//SetTextCallbackandcallsitselfasynchronouslyusingthe
//Invokemethod.
//
//Ifthecallingthreadisthesameasthethreadthatcreated
//theTextBoxcontrol,theTextpropertyissetdirectly.
privatevoidSetText(stringtext)
{
//InvokeRequiredrequiredcomparesthethreadIDofthe
//callingthreadtothethreadIDofthecreatingthread.
//Ifthesethreadsaredifferent,itreturnstrue.
if(this.textBox1.InvokeRequired)
{
SetTextCallbackd=newSetTextCallback(SetText);
this.Invoke(d,newobject[]{text});
}
else
{
this.textBox1.Text=text;
}
}
//Thiseventhandlerstartstheform's
//BackgroundWorkerbycallingRunWorkerAsync.
//
//TheTextpropertyoftheTextBoxcontrolisset
//whentheBackgroundWorkerraisestheRunWorkerCompleted
//event.
privatevoidsetTextBackgroundWorkerBtn_Click(
objectsender,
EventArgse)
{
this.backgroundWorker1.RunWorkerAsync();
}
//ThiseventhandlersetstheTextpropertyoftheTextBox
//control.Itiscalledonthethreadthatcreatedthe
//TextBoxcontrol,sothecallisthread-safe.
//
//BackgroundWorkeristhepreferredwaytoperformasy