本实例是在vs.net2003帮助文档(演练:利用 Visual C# 创作简单的多线程组件)的基础上进行改进,用于更直观的比较采用多线程所带来的效果.
1. 新建一个项目,新建一个组件C_AddCalcu ,添加两个窗体Form1,Form2 下面的附录为相应的代码文件.
2. 运行该项目.对比运行效果,可能发现运行多线程进行计算前后对窗体的组件进行操作有好的响应效果,而未采用的在点击Form2"开始计算"按钮至计算结束,对窗体的所有操作均失出响应. 直观的表现是进度条停止滚动.
---组件C_AddCalcu代码---
using System;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
namespace ThreadSample
{
/// <summary>
/// C_AddCalcu 的摘要说明。
/// </summary>
public class C_AddCalcu : System.ComponentModel.Component
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null;
public C_AddCalcu(System.ComponentModel.IContainer container)
{
///
/// Windows.Forms 类撰写设计器支持所必需的
///
container.Add(this);
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}
public C_AddCalcu()
{
///
/// Windows.Forms 类撰写设计器支持所必需的
///
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
组件设计器生成的代码
double num1=0;
public double GsNum
{
set{ num1=value;}
get{return num1;}
}
//为事件声明委托,组件将使用这些事件向窗体传递值。
public delegate void AddCalcuCompleteHandler(int SubResult, double TotalCalculations);
//声明组件将用来与应用程序进行通信的事件
public event AddCalcuCompleteHandler AddCalcuComplete;
//向组件添加一个(或多个)执行线程,本例只添加一个
public System.Threading.Thread AddCalcuThread;
public void AddCalcu()
{
double varTotalAsOfNow = 0;
int varSubResult = 0;
for(int i=1;i<this.GsNum ;i++)
{
varSubResult+=i;
for(int j=1;j<i;j++)
{
lock(this)
{
varTotalAsOfNow += varSubResult;
}
}
}
AddCalcuComplete(varSubResult, varTotalAsOfNow);
}
//varTotalCalculations += 1;
//varTotalAsOfNow = varTotalCalculations;
//这两行代码递增公共变量 varTotalCalculations 并将局部变量 varTotalAsOfNow 设为此值。
//然后,该值被返回给 frmCalculations,并显示在标签控件中。但返回的值正确吗?如果只有单个执行线程在运行,
//则答案明显是正确的。但是如果有多个线程在运行,答案则变得不太确定。
//每个线程都具有递增变量 varTotalCalculations 的能力。有可能出现这样的情况:在一个线程递增该变量之后,
//但在它将该值复制到 varTotalAsOfNow 之前,另一个线程可能通过递增该变量而更改它的值。
//这将导致有可能每个线程实际上在报告不正确的结果。Visual C# 提供 lock 语句语句以允许线程的同步,
//从而确保每个线程始终返回准确的结果。
//lock 的语法如下所示:
//{
// Insert code that affects the object.
// Insert more code that affects the object.
// Insert more code that affects the object.
// Release the lock.
// }
//输入 lock 块后,在指定的线程对所讨论的对象拥有专用锁之前,对指定表达式的执行一直被堵塞。
//在上面显示的示例中,对 AnObject 的执行处于锁定状态。必须对返回引用的对象(而非返回值的对象)
//使用 lock。然后,执行以块的形式继续进行,而不会受到其他线程的干扰。作为一个单元执行的语句集称为“原子”。
//当遇到 } 时,表达式将被释放,线程可继续正常工作。
// 修改此代码,使其显示为如下形式:
// lock(this)
// {
// varTotalCalculations += 1;
// varTotalAsOfNow = varTotalCalculations;
// }
// 您可能注意到对程序性能的细微影响。这是因为当组件获得排他锁后,线程的执行停止。尽管它保证了正确性,
// 但这种方法抵消了多线程带来的某些性能优点。应该认真考虑锁定线程的必要性,并且仅当绝对必要时才予以实现。
//为组件添加方法
//当实例化 Thread 对象时,它要求一个 ThreadStart 对象形式的参数。
//ThreadStart 对象是一个指向开始线程的方法的地址的委托。
//ThreadStart 对象不能接受参数或者传递值,因此只能表示 void 方法。
//刚才实现的 ChooseThreads 方法将从调用它的程序接收一个值,并使用该值来确定要启动的适当线程。
public void execThread(int flag)
{
if(flag==1)
{
AddCalcuThread = new System.Threading.Thread(new
System.Threading.ThreadStart(this.AddCalcu));
AddCalcuThread.Start();
}
}
}
}
---Form1代码---
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace ThreadSample
{
/// <summary>
/// Form1 的摘要说明。
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button button1;
private System.ComponentModel.IContainer components;
private System.Windows.Forms.TextBox textBox3;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Button button2;
C_AddCalcu C_AddCal;
public Form1()
{
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
C_AddCal=new C_AddCalcu();
//处理窗体将从 C_AddCal 接收的自定义事件
C_AddCal.AddCalcuComplete+=new ThreadSample.C_AddCalcu.AddCalcuCompleteHandler(C_AddCal_AddCalcuComplete);
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
Windows 窗体设计器生成的代码
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void Form1_Load(object sender, System.EventArgs e)
{
this.timer1.Enabled =true;
}
# region "封送处理对控件的调用"
//现在将加速窗体上的显示更新。鉴于控件总是由主执行线程所有,从属线程中对控件的任何调用都需要“封送处理”调用。
//封送处理是跨线程边界移动调用的行为,需要耗费大量的资源。为了使需要发生的封送处理量减到最少,
//并确保以线程安全方式处理调用,应使用 Control.BeginInvoke 方法来调用主执行线程上的方法,从而使必须发生的跨
//线程边界的封送处理量减到最少。当调用操作控件的方法时,这种调用非常必要。有关详细信息,请参见从线程操作控件。
//Invoke 和 BeginInvoke 需要将适当方法的委托作为参数。这些代码行声明一些委托签名,
//这些签名将被 BeginInvoke 用于调用适当的方法。
public delegate void ACHandler(int FormSubResult, double FromTotalCalculations);
//添加下面的方法
private void AddCHandler(int FormSubResult, double FromTotalCalculations)
{
this.textBox2.Text = FormSubResult.ToString();
this.textBox3.Text = FromTotalCalculations.ToString();
this.button1.Enabled =true;
}
//处理窗体将从 Calculator1 接收的事件
private void C_AddCal_AddCalcuComplete(int FormSubResult, double FromTotalCalculations)
{
//调用 BeginInvoke 方法以异步调用这些方法。可以从窗体 (this) 或者窗体上的任何控件调用 BeginInvoke。
this.BeginInvoke(new ACHandler(AddCHandler), new Object[]
{FormSubResult, FromTotalCalculations});
}
//看起来似乎事件处理程序仅仅是对下一个方法进行调用。实际上,该事件处理程序实现了在主操作线程上调用方法。
//这种方法可节省跨线程边界的调用,并使多线程应用程序能够有效运行而不必担心导致死锁。
//有关在多线程环境下使用控件的详细信息,请参见从线程操作控件。
# endregion
private void button1_Click(object sender, System.EventArgs e)
{
C_AddCal.GsNum =Convert.ToDouble(this.textBox1.Text ) ;
//调用 C_AddCal.execThread 方法.
C_AddCal.execThread(1) ;
this.button1.Enabled =false;
}
int i=0;
private void timer1_Tick(object sender, System.EventArgs e)
{
i+=1;
i=i%100;
this.progressBar1.Value =i;
}
private void button2_Click(object sender, System.EventArgs e)
{
Form2 frm2=new Form2();
frm2.Show();
}
}
}
---Form2代码---
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace ThreadSample
{
/// <summary>
/// Form2 的摘要说明。
/// </summary>
public class Form2 : System.Windows.Forms.Form
{
private System.Windows.Forms.TextBox textBox3;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Timer timer1;
private System.ComponentModel.IContainer components;
public Form2()
{
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
Windows 窗体设计器生成的代码
private void button1_Click(object sender, System.EventArgs e)
{
this.button1.Enabled=false;
varRang=Convert.ToInt32 (this.textBox1.Text ) ;
AddCalcu();
this.textBox2.Text = varSubResult.ToString();
this.textBox3.Text = varTotalAsOfNow.ToString();
}
double varTotalAsOfNow = 0;
int varSubResult = 0;
int varRang=10000;
private void AddCalcu()
{
varTotalAsOfNow = 0;
varSubResult = 0;
for(int i=1;i<varRang ;i++)
{
varSubResult+=i;
for(int j=1;j<i;j++)
{
lock(this)
{
varTotalAsOfNow += varSubResult;
}
}
}
this.button1.Enabled =true;
}
int i=0;
private void timer1_Tick(object sender, System.EventArgs e)
{
i+=1;
i=i%100;
this.progressBar1.Value =i;
}
private void Form2_Load(object sender, System.EventArgs e)
{
this.timer1.Enabled =true;
}
}
}
2. 运行该项目.对比运行效果,可能发现运行多线程进行计算前后对窗体的组件进行操作有好的响应效果,而未采用的在点击Form2"开始计算"按钮至计算结束,对窗体的所有操作均失出响应. 直观的表现是进度条停止滚动.
---组件C_AddCalcu代码---
using System;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
namespace ThreadSample
{
/// <summary>
/// C_AddCalcu 的摘要说明。
/// </summary>
public class C_AddCalcu : System.ComponentModel.Component
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null;
public C_AddCalcu(System.ComponentModel.IContainer container)
{
///
/// Windows.Forms 类撰写设计器支持所必需的
///
container.Add(this);
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}
public C_AddCalcu()
{
///
/// Windows.Forms 类撰写设计器支持所必需的
///
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
组件设计器生成的代码
double num1=0;
public double GsNum
{
set{ num1=value;}
get{return num1;}
}
//为事件声明委托,组件将使用这些事件向窗体传递值。
public delegate void AddCalcuCompleteHandler(int SubResult, double TotalCalculations);
//声明组件将用来与应用程序进行通信的事件
public event AddCalcuCompleteHandler AddCalcuComplete;
//向组件添加一个(或多个)执行线程,本例只添加一个
public System.Threading.Thread AddCalcuThread;
public void AddCalcu()
{
double varTotalAsOfNow = 0;
int varSubResult = 0;
for(int i=1;i<this.GsNum ;i++)
{
varSubResult+=i;
for(int j=1;j<i;j++)
{
lock(this)
{
varTotalAsOfNow += varSubResult;
}
}
}
AddCalcuComplete(varSubResult, varTotalAsOfNow);
}
//varTotalCalculations += 1;
//varTotalAsOfNow = varTotalCalculations;
//这两行代码递增公共变量 varTotalCalculations 并将局部变量 varTotalAsOfNow 设为此值。
//然后,该值被返回给 frmCalculations,并显示在标签控件中。但返回的值正确吗?如果只有单个执行线程在运行,
//则答案明显是正确的。但是如果有多个线程在运行,答案则变得不太确定。
//每个线程都具有递增变量 varTotalCalculations 的能力。有可能出现这样的情况:在一个线程递增该变量之后,
//但在它将该值复制到 varTotalAsOfNow 之前,另一个线程可能通过递增该变量而更改它的值。
//这将导致有可能每个线程实际上在报告不正确的结果。Visual C# 提供 lock 语句语句以允许线程的同步,
//从而确保每个线程始终返回准确的结果。
//lock 的语法如下所示:
//{
// Insert code that affects the object.
// Insert more code that affects the object.
// Insert more code that affects the object.
// Release the lock.
// }
//输入 lock 块后,在指定的线程对所讨论的对象拥有专用锁之前,对指定表达式的执行一直被堵塞。
//在上面显示的示例中,对 AnObject 的执行处于锁定状态。必须对返回引用的对象(而非返回值的对象)
//使用 lock。然后,执行以块的形式继续进行,而不会受到其他线程的干扰。作为一个单元执行的语句集称为“原子”。
//当遇到 } 时,表达式将被释放,线程可继续正常工作。
// 修改此代码,使其显示为如下形式:
// lock(this)
// {
// varTotalCalculations += 1;
// varTotalAsOfNow = varTotalCalculations;
// }
// 您可能注意到对程序性能的细微影响。这是因为当组件获得排他锁后,线程的执行停止。尽管它保证了正确性,
// 但这种方法抵消了多线程带来的某些性能优点。应该认真考虑锁定线程的必要性,并且仅当绝对必要时才予以实现。
//为组件添加方法
//当实例化 Thread 对象时,它要求一个 ThreadStart 对象形式的参数。
//ThreadStart 对象是一个指向开始线程的方法的地址的委托。
//ThreadStart 对象不能接受参数或者传递值,因此只能表示 void 方法。
//刚才实现的 ChooseThreads 方法将从调用它的程序接收一个值,并使用该值来确定要启动的适当线程。
public void execThread(int flag)
{
if(flag==1)
{
AddCalcuThread = new System.Threading.Thread(new
System.Threading.ThreadStart(this.AddCalcu));
AddCalcuThread.Start();
}
}
}
}
---Form1代码---
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace ThreadSample
{
/// <summary>
/// Form1 的摘要说明。
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button button1;
private System.ComponentModel.IContainer components;
private System.Windows.Forms.TextBox textBox3;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Button button2;
C_AddCalcu C_AddCal;
public Form1()
{
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
C_AddCal=new C_AddCalcu();
//处理窗体将从 C_AddCal 接收的自定义事件
C_AddCal.AddCalcuComplete+=new ThreadSample.C_AddCalcu.AddCalcuCompleteHandler(C_AddCal_AddCalcuComplete);
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
Windows 窗体设计器生成的代码
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void Form1_Load(object sender, System.EventArgs e)
{
this.timer1.Enabled =true;
}
# region "封送处理对控件的调用"
//现在将加速窗体上的显示更新。鉴于控件总是由主执行线程所有,从属线程中对控件的任何调用都需要“封送处理”调用。
//封送处理是跨线程边界移动调用的行为,需要耗费大量的资源。为了使需要发生的封送处理量减到最少,
//并确保以线程安全方式处理调用,应使用 Control.BeginInvoke 方法来调用主执行线程上的方法,从而使必须发生的跨
//线程边界的封送处理量减到最少。当调用操作控件的方法时,这种调用非常必要。有关详细信息,请参见从线程操作控件。
//Invoke 和 BeginInvoke 需要将适当方法的委托作为参数。这些代码行声明一些委托签名,
//这些签名将被 BeginInvoke 用于调用适当的方法。
public delegate void ACHandler(int FormSubResult, double FromTotalCalculations);
//添加下面的方法
private void AddCHandler(int FormSubResult, double FromTotalCalculations)
{
this.textBox2.Text = FormSubResult.ToString();
this.textBox3.Text = FromTotalCalculations.ToString();
this.button1.Enabled =true;
}
//处理窗体将从 Calculator1 接收的事件
private void C_AddCal_AddCalcuComplete(int FormSubResult, double FromTotalCalculations)
{
//调用 BeginInvoke 方法以异步调用这些方法。可以从窗体 (this) 或者窗体上的任何控件调用 BeginInvoke。
this.BeginInvoke(new ACHandler(AddCHandler), new Object[]
{FormSubResult, FromTotalCalculations});
}
//看起来似乎事件处理程序仅仅是对下一个方法进行调用。实际上,该事件处理程序实现了在主操作线程上调用方法。
//这种方法可节省跨线程边界的调用,并使多线程应用程序能够有效运行而不必担心导致死锁。
//有关在多线程环境下使用控件的详细信息,请参见从线程操作控件。
# endregion
private void button1_Click(object sender, System.EventArgs e)
{
C_AddCal.GsNum =Convert.ToDouble(this.textBox1.Text ) ;
//调用 C_AddCal.execThread 方法.
C_AddCal.execThread(1) ;
this.button1.Enabled =false;
}
int i=0;
private void timer1_Tick(object sender, System.EventArgs e)
{
i+=1;
i=i%100;
this.progressBar1.Value =i;
}
private void button2_Click(object sender, System.EventArgs e)
{
Form2 frm2=new Form2();
frm2.Show();
}
}
}
---Form2代码---
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace ThreadSample
{
/// <summary>
/// Form2 的摘要说明。
/// </summary>
public class Form2 : System.Windows.Forms.Form
{
private System.Windows.Forms.TextBox textBox3;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Timer timer1;
private System.ComponentModel.IContainer components;
public Form2()
{
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
Windows 窗体设计器生成的代码
private void button1_Click(object sender, System.EventArgs e)
{
this.button1.Enabled=false;
varRang=Convert.ToInt32 (this.textBox1.Text ) ;
AddCalcu();
this.textBox2.Text = varSubResult.ToString();
this.textBox3.Text = varTotalAsOfNow.ToString();
}
double varTotalAsOfNow = 0;
int varSubResult = 0;
int varRang=10000;
private void AddCalcu()
{
varTotalAsOfNow = 0;
varSubResult = 0;
for(int i=1;i<varRang ;i++)
{
varSubResult+=i;
for(int j=1;j<i;j++)
{
lock(this)
{
varTotalAsOfNow += varSubResult;
}
}
}
this.button1.Enabled =true;
}
int i=0;
private void timer1_Tick(object sender, System.EventArgs e)
{
i+=1;
i=i%100;
this.progressBar1.Value =i;
}
private void Form2_Load(object sender, System.EventArgs e)
{
this.timer1.Enabled =true;
}
}
}