Windows 表單控制項進行安全執行緒調用

來源:互聯網
上載者:User
Windows表單控制項進行安全執行緒調用 [caven 發表於 2006-4-8 16:49:32]
用Visual Studio 2005寫了個很簡單的程式,主要是在主表單外建立了一個線程,讓它專門重新整理進度條。曾經在Visual Studio 2003裡寫過這個程式並且可以正常啟動並執行,2005裡卻出現了異常。“線程間操作無效:不是從建立它的線程裡訪問。”
哦,後來才發現這是2005的新特點。。

對 Windows 表單控制項進行安全執行緒調用

使用多線程提高 Windows 表單應用程式的效能時,必須注意以安全執行緒方式調用控制項。
樣本
訪問 Windows 表單控制項本質上不是安全執行緒的。如果有兩個或多個線程操作某一控制項的狀態,則可能會迫使該控制項進入一種不一致的狀態。還可能出現其他與線程相關的 bug,包括爭用情況和死結。確保以安全執行緒方式訪問控制項非常重要。

.NET Framework 有助於在以非安全執行緒方式訪問控制項時檢測到這一問題。在調試器中運行應用程式時,如果建立某控制項的線程之外的其他線程試圖調用該控制項,則調試器會引發一個 InvalidOperationException,並提示訊息:“從不是建立控制項 control name 的線程訪問它。”

此異常在調試期間和運行時的某些情況下可靠地發生。強烈建議您在顯示此錯誤資訊時修複此問題。在調試以 .NET Framework 2.0 版之前的 .NET Framework 編寫的應用程式時,可能會出現此異常。

注意
可以通過將 CheckForIllegalCrossThreadCalls 屬性的值設定為 false 來禁用此異常。這會使控制項以與在 Visual Studio 2003 下相同的方式運行。

下面的程式碼範例示範如何從輔助線程以安全執行緒方式和非安全執行緒方式調用 Windows 表單控制項。它示範一種以非安全執行緒方式設定 TextBox 控制項的 Text 屬性的方法,還示範兩種以安全執行緒方式設定 Text 屬性的方法。

程式碼using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
//http://msdn2.microsoft.com/zh-cn/library/ms171728.aspx#Mtps_DropDownFilterText
namespace CrossThreadDemo
{
public class Form1 : Form
{

//這個代理能非同步呼叫設定文字框
delegate void SetTextCallback(string text);

//這個線程是用來示範2個安全線程和不安全線程調用表單控建的。
private Thread demoThread = null;

//一個後台工作群組件,用它可以進行非同步作業
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);
}

//非安全線程觸發這個方法。
private void setTextUnsafeBtn_Click(
object sender,
EventArgs e)
{//建立一個線程,調用ThreadProcUnsafe方法
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()
{//安全執行方法一:(1。通過調用一個SetText方法來設定TextBox的文字。)
this.SetText("This text was set safely.");
}

private void SetText(string text)
{
//這個方法使用了InvokeRequired來判斷調用是否來自其他線程。
if (this.textBox1.InvokeRequired)//如果來自非主線程,則執行下面
{
SetTextCallback d = new SetTextCallback(SetText);//利用代理,讓代理等於這個方法本身。
this.Invoke(d, new object[] { text });//讓主表單線程調用這個方法。
}
else//當主表單調用此方法時,InvokeRequired為false,則可以在主表單線程執行修改文字框了。
{
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.

//以下是利用組件BackgroundWorker來進行非同步呼叫。
private void setTextBackgroundWorkerBtn_Click(
object sender,
EventArgs e)
{//當按鈕被按下以後,調用組件的RunWorkerAsyne()方法告訴組件:開始執行後台操作。
this.backgroundWorker1.RunWorkerAsync();
//backgroundWorker會在新的線程執行backgroundWorker1.DoWork的內容。
//如果沒有DoWork動作,則立刻會回到當前線程執行backgroundWorker1.RunWorkerCompleted
}

// 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());
}

}
}

我另外自己寫了個,BackgroundWorker更詳細的應用。
Form1.cs 程式碼using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace safeThread
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
delegate void del(int i);
Thread ThreadDemo;
private void button1_Click(object sender, EventArgs e)
{
ThreadDemo = new Thread(new ThreadStart (ThreadProSafe));
ThreadDemo.Start();
toolStripStatusLabel2.Text = "運算進行中中。。";
button1.Text = "運算中..";
this.Cursor = Cursors.WaitCursor;
//this.Dispose(false);
}

private void ThreadProSafe()
{
try
{
for (int i = 1; i <= 500; i++)
{
Console.WriteLine(i);
SetProcess((int)(i / 5));
Thread.Sleep(5);
}
}
finally
{
SetSatus(0);
}

}

private void SetProcess(int vi)
{
if (InvokeRequired)
{
del dd = new del(SetProcess);
this.Invoke(dd, new object[] { vi });
}
else
{
if(vi<=100)
this.progressBar1.Value = vi;

}

//CheckForIllegalCrossThreadCalls

}
private void SetSatus(int vi)
{
if (InvokeRequired)
{
del dd = new del(SetSatus);
this.Invoke(dd, new object[] { vi });
}
else
{
toolStripStatusLabel2.Text = "運算結束!";

button1.Text = "開始運算";

this.Cursor = Cursors.Default;

}

}

private void button2_Click(object sender, EventArgs e)
{
if (((Button)sender).Text == "後台組件運算")
{
this.backgroundWorker1.RunWorkerAsync(10);
this.button2.Text = "取消後台運算";
this.toolStripStatusLabel2.Text = "正在通過組件後台線程運算。。";
this.Cursor = Cursors.WaitCursor;

}
else
{
backgroundWorker1.CancelAsync();
this.toolStripStatusLabel2.Text = "正在取消。。";

}

//MessageBox.Show("OnClick:" + System.AppDomain.GetCurrentThreadId());
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 500; i++)
{
Console.WriteLine(i);
backgroundWorker1.ReportProgress((int)(i / 5));
Thread.Sleep(5);
if (backgroundWorker1.CancellationPending)
{

e.Cancel = true;

break;
}
}

//這裡是需要後台啟動並執行程式
}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
toolStripStatusLabel2.Text = "運算已經取消!";
}
else
{
toolStripStatusLabel2.Text = "運算結束!";
}

button2.Text = "後台組件運算";
this.Cursor = Cursors.Default;
Console.WriteLine("aaa");
//MessageBox.Show("RunWorkerCompleted:" + System.AppDomain.GetCurrentThreadId());
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}

}
}

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.