c#多线程编程海南摄影工作室

来源:互联网 发布:php laravel csrf 编辑:程序博客网 时间:2024/04/26 21:55
c#多线程编程程序设计 2007-05-12 17:54:13 浏览2565 评论0 字号:大中小 订阅

  使用多线程进步 Windows 窗体应用程序的机能时,必需注意以线程安全方式调用控件。

  示例访问 Windows 窗体控件实质上不是线程安全的。如果有两个或多个线程操作某一控件的状况,则可能会迫使该控件进入一种不一致的状态。还可能呈现其余与线程相关的 bug,包含争用情况和逝世锁。确保以线程安全方式访问控件无比主要。

  .NET Framework 有助于在以非线程安全方式访问控件时检测到这一问题。在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个 InvalidOperationException,并提醒新闻:“从不是创建控件 control name 的线程访问它。”

  此异常在调试期间和运行时的某些情况下可靠地发生。强烈倡议您在显示此过错信息时修复此问题。在调试以 .NET Framework 2.0 版之前的 .NET Framework 编写的应用程序时,可能会涌现此异常。

  留神 可以通过将 CheckForIllegalCrossThreadCalls 属性的值设置为 false 来禁用此异样。这会使控件以与在 Visual Studio 2003 下雷同的方式运行。 .net 2005为了增添安全性,在默认情况下,多线程操作时,一个线程是不可能修改另外一个线程创建的控件的,而在2003仿佛是可以的。线程间空间访问的惯例方法那就是用代理(Delegate),执行invoke方法,这是.net所推荐的方法,详细用法先容的文章许多,就不再赘述。

  用代办进行线程间拜访时,对每一个跨线程控件的修正都要定义相干的署理方法,绝对麻烦一些,如果在对体系安全性请求不高的情况下能够用一种简略的方法:在创建空间的窗体结构函数中增加如下代码:

  System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;

  即不进行跨线程正当性检讨,这样固然是.net所不推举的方法,然而简便了良多。

  下面的代码示例演示如何从辅助线程以线程安全方式和非线程安全方式调用 Windows 窗体控件。它演示一种以非线程安全方式设置 TextBox 控件的 Text 属性的方法,还演示两种以线程安全方式设置 Text 属性的方法。

  Visual Basic 复制代码Imports SystemImports System.ComponentModelImports System.ThreadingImports System.Windows.Forms

  Public Class Form1 Inherits Form ' This delegate enables asynchronous calls for setting ' the text property on a TextBox control. Delegate Sub SetTextCallback([text] As String)

  ' This thread is used to demonstrate both thread-safe and ' unsafe ways to call a Windows Forms control. Private demoThread As Thread = Nothing

  ' This BackgroundWorker is used to demonstrate the ' preferred way of performing asynchronous operations. Private WithEvents backgroundWorker1 As BackgroundWorker

  Private textBox1 As TextBox Private WithEvents setTextUnsafeBtn As Button Private WithEvents setTextSafeBtn As Button Private WithEvents setTextBackgroundWorkerBtn As Button Private components As System.ComponentModel.IContainer = Nothing Public Sub New() InitializeComponent() End Sub Protected Overrides Sub Dispose(disposing As Boolean) If disposing AndAlso Not (components Is Nothing) Then components.Dispose() End If MyBase.Dispose(disposing) End Sub ' This event handler creates a thread that calls a ' Windows Forms control in an unsafe way. Private Sub setTextUnsafeBtn_Click( _ ByVal sender As Object, _ ByVal e As EventArgs) Handles setTextUnsafeBtn.Click

  Me.demoThread = New Thread( _ New ThreadStart(AddressOf Me.ThreadProcUnsafe))

  Me.demoThread.Start() End Sub ' This method is executed on the worker thread and makes ' an unsafe call on the TextBox control. Private Sub ThreadProcUnsafe() Me.textBox1.Text = "This text was set unsafely." End Sub

  ' This event handler creates a thread that calls a ' Windows Forms control in a thread-safe way. Private Sub setTextSafeBtn_Click( _ ByVal sender As Object, _ ByVal e As EventArgs) Handles setTextSafeBtn.Click

  Me.demoThread = New Thread( _ New ThreadStart(AddressOf Me.ThreadProcSafe))

  Me.demoThread.Start() End Sub ' This method is executed on the worker thread and makes ' a thread-safe call on the TextBox control. Private Sub ThreadProcSafe() Me.SetText("This text was set safely.") End Sub

  ' 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 Sub SetText(ByVal [text] As String)

  ' 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 Me.textBox1.InvokeRequired Then Dim d As New SetTextCallback(AddressOf SetText) Me.Invoke(d, New Object() {[text]}) Else Me.textBox1.Text = [text] End If End Sub

  ' 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 Sub setTextBackgroundWorkerBtn_Click( _ ByVal sender As Object,海南摄影工作室, _ ByVal e As EventArgs) Handles setTextBackgroundWorkerBtn.Click Me.backgroundWorker1.RunWorkerAsync() End Sub ' 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 Sub backgroundWorker1_RunWorkerCompleted( _ ByVal sender As Object, _ ByVal e As RunWorkerCompletedEventArgs) _ Handles backgroundWorker1.RunWorkerCompleted Me.textBox1.Text = _ "This text was set safely by BackgroundWorker." End Sub

  #Region "Windows Form Designer generated code" Private Sub InitializeComponent() Me.textBox1 = New System.Windows.Forms.TextBox() Me.setTextUnsafeBtn = New System.Windows.Forms.Button() Me.setTextSafeBtn = New System.Windows.Forms.Button() Me.setTextBackgroundWorkerBtn = New System.Windows.Forms.Button() Me.backgroundWorker1 = New System.ComponentModel.BackgroundWorker() Me.SuspendLayout() ' ' textBox1 ' Me.textBox1.Location = New System.Drawing.Point(12, 12) Me.textBox1.Name = "textBox1" Me.textBox1.Size = New System.Drawing.Size(240, 20) Me.textBox1.TabIndex = 0 ' ' setTextUnsafeBtn ' Me.setTextUnsafeBtn.Location = New System.Drawing.Point(15, 55) Me.setTextUnsafeBtn.Name = "setTextUnsafeBtn" Me.setTextUnsafeBtn.TabIndex = 1 Me.setTextUnsafeBtn.Text = "Unsafe Call" ' ' setTextSafeBtn ' Me.setTextSafeBtn.Location = New System.Drawing.Point(96, 55) Me.setTextSafeBtn.Name = "setTextSafeBtn" Me.setTextSafeBtn.TabIndex = 2 Me.setTextSafeBtn.Text = "Safe Call" ' ' setTextBackgroundWorkerBtn ' Me.setTextBackgroundWorkerBtn.Location = New System.Drawing.Point(177, 55) Me.setTextBackgroundWorkerBtn.Name = "setTextBackgroundWorkerBtn" Me.setTextBackgroundWorkerBtn.TabIndex = 3 Me.setTextBackgroundWorkerBtn.Text = "Safe BW Call" ' ' backgroundWorker1 ' ' ' Form1 ' Me.ClientSize = New System.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() End Sub 'InitializeComponent #End Region <STAThread()> _ Shared Sub Main() Application.EnableVisualStyles() Application.Run(New Form1()) End SubEnd Class

  C# 复制代码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()); }

  }}

  对 Windows 窗体控件的非线程安全调用对 Windows 窗体控件的非线程平安调用方式是从帮助线程直接调用。调用运用程序时,调试器会引发一个 InvalidOperationException,三亚摄影,忠告对控件的调用不是线程保险的。

  Visual Basic 复制代码' This event handler creates a thread that calls a ' Windows Forms control in an unsafe way. Private Sub setTextUnsafeBtn_Click( _ ByVal sender As Object, _ ByVal e As EventArgs) Handles setTextUnsafeBtn.Click

  Me.demoThread = New Thread( _ New ThreadStart(AddressOf Me.ThreadProcUnsafe))

  Me.demoThread.Start() End Sub

  ' This method is executed on the worker thread and makes' an unsafe call on the TextBox control.Private Sub ThreadProcUnsafe() Me.textBox1.Text = "This text was set unsafely."End Sub

  C# 复制代码// 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.";}

  对 Windows 窗体控件的线程安全调用对 Windows 窗体控件进行线程安全调用查问控件的 InvokeRequired 属性。

  如果 InvokeRequired 返回 true,则使用实际调用控件的委托来调用 Invoke。

  如果 InvokeRequired 返回 false,则直接调用控件。

  在下面的代码示例中,此逻辑是在一个称为 SetText 的适用工具方法中实现的。名为 SetTextDelegate 的委托类型封装 SetText 方式。TextBox 控件的 InvokeRequired 返回 true 时,SetText 方法创建 SetTextDelegate 的一个实例,并调用窗体的 Invoke 办法。这使得 SetText 方法被创立 TextBox 控件的线程调用,而且在此线程高低文中将直接设置 Text 属性。

  Visual Basic 复制代码' This event handler creates a thread that calls a ' Windows Forms control in a thread-safe way. Private Sub setTextSafeBtn_Click( _ ByVal sender As Object, _ ByVal e As EventArgs) Handles setTextSafeBtn.Click

  Me.demoThread = New Thread( _ New ThreadStart(AddressOf Me.ThreadProcSafe))

  Me.demoThread.Start() End Sub

  ' This method is executed on the worker thread and makes' a thread-safe call on the TextBox control.Private Sub ThreadProcSafe() Me.SetText("This text was set safely.") End Sub

  C# 复制代码// 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.");}

  Visual Basic 复制代码' 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 Sub SetText(ByVal [text] As String)

  ' 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 Me.textBox1.InvokeRequired Then Dim d As New SetTextCallback(AddressOf SetText) Me.Invoke(d, New Object() {[text]}) Else Me.textBox1.Text = [text] End If End Sub

  C# 复制代码// 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; }}

  使用 BackgroundWorker 进行的线程安全调用在应用程序中实现多线程的首选方式是使用 BackgroundWorker 组件。BackgroundWorker 组件使用事件驱动模型实现多线程。辅助线程运行 DoWork 事件处理程序,创建控件的线程运行 ProgressChanged 和 RunWorkerCompleted 事件处理程序。注意不要从 DoWork 事件处理程序调用你的任何控件。

  下面的代码示例不异步履行任何工作,因而不 DoWork 事件处理程序的实现。TextBox 控件的 Text 属性在 RunWorkerCompleted 事件处理程序中直接设置。

  Visual Basic 复制代码' 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 Sub setTextBackgroundWorkerBtn_Click( _ ByVal sender As Object, _ ByVal e As EventArgs) Handles setTextBackgroundWorkerBtn.Click Me.backgroundWorker1.RunWorkerAsync() End Sub

  ' 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 Sub backgroundWorker1_RunWorkerCompleted( _ ByVal sender As Object, _ ByVal e As RunWorkerCompletedEventArgs) _ Handles backgroundWorker1.RunWorkerCompleted Me.textBox1.Text = _ "This text was set safely by BackgroundWorker." End Sub

  C# 复制代码// 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.";}

  Windows 窗体上的 ActiveX 控件假如在窗体上应用 ActiveX 控件,则在调试器下运行时可能会收到线程间 InvalidOperationException。产生这种情形时,ActiveX 控件不支撑多线程处置。有关使用 Windows 窗体的 ActiveX 控件的更多信息,请参见 Windows 窗体跟非托管利用程序。

  如果您使用的是 Visual Studio,则可以通过禁用 Visual Studio 宿主进程来避免此异常发生。

  如何:禁用宿主过程

  牢靠编程警告 使用任何一种多线程时,代码都轻易发生十分重大而庞杂的 bug。有关更多信息,请在实现使用多线程的任何解决计划之前参见托管线程处理的最佳做法。

  请参见义务如何:在后台运行操作如何:实现使用后盾操作的窗体

  参考BackgroundWorker

  其他资源使用 .NET Framework 开发自定义 Windows 窗体控件Windows 窗体和非托管应用程序

  NavigationMSDN Library > Visual Studio 2005 > Visual Studio 文档 > 基于 Windows 的应用程序、组件和服务 > 创建基于 Windows 的应用程序 > Windows 窗体 > Windows 窗体入门 > Windows 窗体控件 > 使用 .NET Framework 开发自定义 Windows 窗体控件 > Windows 窗体控件中的多线程处理 > 如何:对 Windows 窗体控件进行线程安全调用

原创粉丝点击