C#如何解决跨线程调用窗体控件引发的线程安全问题-创新互联
这篇文章将为大家详细讲解有关C#如何解决跨线程调用窗体控件引发的线程安全问题,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
成都创新互联公司坚持“要么做到,要么别承诺”的工作理念,服务领域包括:做网站、网站建设、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的丘北网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!如何:对 Windows 窗体控件进行线程安全调用
访问 Windows 窗体控件本质上不是线程安全的。 如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。 还可能会出现其他与线程相关的 Bug,例如争用情况和死锁。 确保以线程安全方式访问控件非常重要。
在未使用 Invoke 方法的情况下,从不是创建某个控件的线程的其他线程调用该控件是不安全的。 以下非线程安全的调用的示例。
// This event handler creates a thread that calls a // Windows Forms control in an unsafe way. private void setTextUnsafeBtn_Click( object sender, EventArgs e) { this.demoThread = new Thread(new ThreadStart(this.ThreadProcUnsafe)); this.demoThread.Start(); } // This method is executed on the worker thread and makes // an unsafe call on the TextBox control. private void ThreadProcUnsafe() { this.textBox1.Text = "This text was set unsafely."; }
.NET Framework 可帮助您检测以非线程安全方式访问控件这一问题。 在调试器中运行应用程序时,如果一个不是创建某个控件的线程的其他线程调用该控件,则调试器会引发一个 InvalidOperationException,并显示以下消息:“从不是创建控件控件名称 的线程访问它。”
此异常在调试期间和运行时的某些情况下可靠地发生。 在调试以 .NET Framework 2.0 版之前的 .NET Framework 编写的应用程序时,可能会出现此异常。 我们强烈建议您在发现此问题时进行修复,但您可以通过将 CheckForIllegalCrossThreadCalls 属性设置为 false 来禁用它。(不推荐)
对 Windows 窗体控件进行线程安全调用
查询控件的 InvokeRequired 属性。
如果 InvokeRequired 返回 true,则使用实际调用控件的委托来调用 Invoke。
如果 InvokeRequired 返回 false,则直接调用控件。
在下面的代码示例中,将在由后台线程执行的 ThreadProcSafe 方法中实现线程安全调用。 如果 TextBox 控件的 InvokeRequired 返回 true,则 ThreadProcSafe 方法会创建 SetTextCallback 的一个实例,并将该实例传递给窗体的 Invoke 方法。 这使得 SetText 方法被创建 TextBox 控件的线程调用,而且在此线程上下文中将直接设置 Text 属性。
// This event handler creates a thread that calls a // Windows Forms control in a thread-safe way. private void setTextSafeBtn_Click( object sender, EventArgs e) { this.demoThread = new Thread(new ThreadStart(this.ThreadProcSafe)); this.demoThread.Start(); } // This method is executed on the worker thread and makes // a thread-safe call on the TextBox control. private void ThreadProcSafe() { this.SetText("This text was set safely."); }
using System; using System.ComponentModel; using System.Threading; using System.Windows.Forms; namespace CrossThreadDemo { public class Form1 : Form { // This delegate enables asynchronous calls for setting // the text property on a TextBox control. delegate void SetTextCallback(string text); // This thread is used to demonstrate both thread-safe and // unsafe ways to call a Windows Forms control. private Thread demoThread = null; // This BackgroundWorker is used to demonstrate the // preferred way of performing asynchronous operations. private BackgroundWorker backgroundWorker1; private TextBox textBox1; private Button setTextUnsafeBtn; private Button setTextSafeBtn; private Button setTextBackgroundWorkerBtn; private System.ComponentModel.IContainer components = null; public Form1() { InitializeComponent(); } protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } // This event handler creates a thread that calls a // Windows Forms control in an unsafe way. private void setTextUnsafeBtn_Click( object sender, EventArgs e) { this.demoThread = new Thread(new ThreadStart(this.ThreadProcUnsafe)); this.demoThread.Start(); } // This method is executed on the worker thread and makes // an unsafe call on the TextBox control. private void ThreadProcUnsafe() { this.textBox1.Text = "This text was set unsafely."; } // This event handler creates a thread that calls a // Windows Forms control in a thread-safe way. private void setTextSafeBtn_Click( object sender, EventArgs e) { this.demoThread = new Thread(new ThreadStart(this.ThreadProcSafe)); this.demoThread.Start(); } // This method is executed on the worker thread and makes // a thread-safe call on the TextBox control. private void ThreadProcSafe() { this.SetText("This text was set safely."); } // This method demonstrates a pattern for making thread-safe // calls on a Windows Forms control. // // If the calling thread is different from the thread that // created the TextBox control, this method creates a // SetTextCallback and calls itself asynchronously using the // Invoke method. // // If the calling thread is the same as the thread that created // the TextBox control, the Text property is set directly. private void SetText(string text) { // InvokeRequired required compares the thread ID of the // calling thread to the thread ID of the creating thread. // If these threads are different, it returns true. if (this.textBox1.InvokeRequired) { SetTextCallback d = new SetTextCallback(SetText); this.Invoke(d, new object[] { text }); } else { this.textBox1.Text = text; } } // This event handler starts the form's // BackgroundWorker by calling RunWorkerAsync. // // The Text property of the TextBox control is set // when the BackgroundWorker raises the RunWorkerCompleted // event. private void setTextBackgroundWorkerBtn_Click( object sender, EventArgs e) { this.backgroundWorker1.RunWorkerAsync(); } // This event handler sets the Text property of the TextBox // control. It is called on the thread that created the // TextBox control, so the call is thread-safe. // // BackgroundWorker is the preferred way to perform asynchronous // operations. private void backgroundWorker1_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e) { this.textBox1.Text = "This text was set safely by BackgroundWorker."; } #region Windows Form Designer generated code private void InitializeComponent() { this.textBox1 = new System.Windows.Forms.TextBox(); this.setTextUnsafeBtn = new System.Windows.Forms.Button(); this.setTextSafeBtn = new System.Windows.Forms.Button(); this.setTextBackgroundWorkerBtn = new System.Windows.Forms.Button(); this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker(); this.SuspendLayout(); // // textBox1 // this.textBox1.Location = new System.Drawing.Point(12, 12); this.textBox1.Name = "textBox1"; this.textBox1.Size = new System.Drawing.Size(240, 20); this.textBox1.TabIndex = 0; // // setTextUnsafeBtn // this.setTextUnsafeBtn.Location = new System.Drawing.Point(15, 55); this.setTextUnsafeBtn.Name = "setTextUnsafeBtn"; this.setTextUnsafeBtn.TabIndex = 1; this.setTextUnsafeBtn.Text = "Unsafe Call"; this.setTextUnsafeBtn.Click += new System.EventHandler(this.setTextUnsafeBtn_Click); // // setTextSafeBtn // this.setTextSafeBtn.Location = new System.Drawing.Point(96, 55); this.setTextSafeBtn.Name = "setTextSafeBtn"; this.setTextSafeBtn.TabIndex = 2; this.setTextSafeBtn.Text = "Safe Call"; this.setTextSafeBtn.Click += new System.EventHandler(this.setTextSafeBtn_Click); // // setTextBackgroundWorkerBtn // this.setTextBackgroundWorkerBtn.Location = new System.Drawing.Point(177, 55); this.setTextBackgroundWorkerBtn.Name = "setTextBackgroundWorkerBtn"; this.setTextBackgroundWorkerBtn.TabIndex = 3; this.setTextBackgroundWorkerBtn.Text = "Safe BW Call"; this.setTextBackgroundWorkerBtn.Click += new System.EventHandler(this.setTextBackgroundWorkerBtn_Click); // // backgroundWorker1 // this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted); // // Form1 // this.ClientSize = new System.Drawing.Size(268, 96); this.Controls.Add(this.setTextBackgroundWorkerBtn); this.Controls.Add(this.setTextSafeBtn); this.Controls.Add(this.setTextUnsafeBtn); this.Controls.Add(this.textBox1); this.Name = "Form1"; this.Text = "Form1"; this.ResumeLayout(false); this.PerformLayout(); } #endregion [STAThread] static void Main() { Application.EnableVisualStyles(); Application.Run(new Form1()); } } }
关于“C#如何解决跨线程调用窗体控件引发的线程安全问题”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。
当前文章:C#如何解决跨线程调用窗体控件引发的线程安全问题-创新互联
本文路径:http://azwzsj.com/article/ddjcpj.html