利用VC# 创作简单的多线程组件

2016-02-19 16:16 4 1 收藏

给自己一点时间接受自己,爱自己,趁着下午茶的时间来学习图老师推荐的利用VC# 创作简单的多线程组件,过去的都会过去,迎接崭新的开始,释放更美好的自己。

【 tulaoshi.com - 编程语言 】

  可以编写能同时执行多个任务的应用程序。此能力(称为多线程处理或自由线程处理)是设计处理器密集型且要求用户输入的组件的强大方法。计算工资表信息的组件就是一个可能利用多线程处理的组件示例。该组件可以在一个线程上处理用户输入到数据库的数据,而在另一个线程上执行频繁使用处理器的工资表计算。通过在不同的线程上运行这些进程,用户不必等到计算机完成计算,就可以输入其他数据。在本演练中,将创建一个简单的多线程组件,该组件可以同时执行若干个复杂计算。

  创建项目

  应用程序将包括单个窗体和一个组件。用户将输入值并指示该组件开始计算。然后,窗体将接收来自该组件的值,将其显示在标签控件中。该组件将执行频繁使用处理器的计算,并在完成后通知窗体。您将在组件中创建公共变量,用以保存从用户界面收到的值。同时,您还将在组件中实现一些方法,根据这些变量的值执行计算。

  注意 尽管对于计算值的方法来说,函数通常更为可取,但不能在线程之间传递参数,也不能返回值。有很多向线程提供值和从线程接收值的简单方法。在本演示中,将通过更新公共变量将值返回到用户界面,当线程执行完毕后,使用事件来通知主程序。

  创建窗体

  创建新的Windows 应用程序项目。

  将应用程序命名为 Calculations,并将 Form1.cs 重命名为 frmCalculations.cs。

  该窗体将用作应用程序的主用户界面。

  双击设计器上的窗体以打开代码编辑器。在编辑菜单中,选择查找和替换,然后选择替换。使用全部替换将 Form1 替换为 frmCalculations。

  在解决方案资源管理器中,右击frmCalculations.cs并选择视图设计器。设计器打开。

  向窗体中添加 5 个 Label 控件、4 个 Button 控件和 1 个 TextBox 控件。

  为这些控件设置属性,如下所示:

  控件名称文本 Label1lblFactorial1(空白)Label2lblFactorial2 (空白) Label3lblAddTwo (空白)Label4lblRunLoops(空白)Label5lblTotalCalculations(空白)Button1btnFactorial1Factorial Button2 btnFactorial2Factorial - 1Button3 btnAddTwoAdd Two Button4btnRunLoopsRun a Loop Textbox1txtValue(空白)

  创建 Calculator 组件

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

  从项目菜单中选择添加组件。

  将组件命名为 Calculator。

  向 Calculator 组件添加公共变量

  为 Calculator 打开代码编辑器。

  添加创建公共变量的语句,这些变量用于将值从 frmCalculations 传递给每个线程。

  变量 varTotalCalculations 将保留该组件执行的计算总数的累计值,而其他变量将接收来自窗体的值。

  

public int varAddTwo;public int varFact1;public int varFact2;public int varLoopValue;public double varTotalCalculations = 0;

  向 Calculator 组件添加方法和事件

  为事件声明委托,组件将使用这些事件向窗体传递值。

  注意 尽管您将声明 4 个事件,但由于其中的两个事件将具有相同的签名,因此只需要创建 3 个委托。

  紧接着上一步输入的变量声明的下方,键入下列代码:

  

// This delegate will be invoked with two of your events.public delegate void FactorialCompleteHandler(double Factorial, double TotalCalculations);public delegate void AddTwoCompleteHandler(int Result, double TotalCalculations);public delegate void LoopCompleteHandler(double TotalCalculations, int Counter);

  声明组件将用来与应用程序进行通信的事件。为实现此目的,紧接着上一步输入的代码的下方,添加下列代码。

  

public event FactorialCompleteHandler FactorialComplete;public event FactorialCompleteHandler FactorialMinusOneComplete;public event AddTwoCompleteHandler AddTwoComplete;public event LoopCompleteHandler LoopComplete;

  紧接着上一步键入的代码的下方,键入下列代码:

  

// This method will calculate the value of a number minus 1 factorial// (varFact2-1!).public void FactorialMinusOne(){double varTotalAsOfNow = 0;double varResult = 1;// Performs a factorial calculation on varFact2 - 1.for (int varX = 1; varX = varFact2 - 1; varX++){varResult *= varX;// Increments varTotalCalculations and keeps track of the current// total as of this instant.varTotalCalculations += 1;varTotalAsOfNow = varTotalCalculations;}// Signals that the method has completed, and communicates the// result and a value of total calculations performed up to this// point.FactorialMinusOneComplete(varResult, varTotalAsOfNow);}// This method will calculate the value of a number factorial.// (varFact1!)public void Factorial(){double varResult = 1;double varTotalAsOfNow = 0;for (int varX = 1; varX = varFact1; varX++){varResult *= varX;varTotalCalculations += 1;varTotalAsOfNow = varTotalCalculations;}FactorialComplete(varResult, varTotalAsOfNow);}// This method will add two to a number (varAddTwo+2).public void AddTwo(){double varTotalAsOfNow = 0;int varResult = varAddTwo + 2;varTotalCalculations += 1;varTotalAsOfNow = varTotalCalculations;AddTwoComplete(varResult, varTotalAsOfNow);}// This method will run a loop with a nested loop varLoopValue times.public void RunALoop(){int varX;double varTotalAsOfNow = 0;for (varX = 1; varX = varLoopValue; varX++){// This nested loop is added solely for the purpose of slowing down// the program and creating a processor-intensive application.for (int varY = 1; varY = 500; varY++){varTotalCalculations += 1;varTotalAsOfNow = varTotalCalculations;}}LoopComplete(varTotalAsOfNow, varLoopValue);}

  将用户输入传输到组件

  下一步是向 frmCalculations 添加代码,以接收用户输入,以及从 Calculator 组件接收值和向它传输值。

  实现 frmCalculations 的前端功能

  在代码编辑器中打开 frmCalculations。

  找到 public class frmCalculations 语句。紧接着 { 的下方键入:

  

Calculator Calculator1;

  找到构造函数。紧接着 } 之前,添加以下行:

  

// Creates a new instance of Calculator.Calculator1 = new Calculator();

  在设计器中单击每个按钮,为每个控件的单击事件处理程序生成代码大纲,并添加代码以创建这些处理程序。

  完成后,单击事件处理程序应该类似于以下形式:

  

private void btnFactorial1_Click(object sender, System.EventArgs e)// Passes the value typed in the txtValue to Calculator.varFact1.{Calculator1.varFact1 = int.Parse(txtValue.Text);// Disables the btnFactorial1 until this calculation is complete.btnFactorial1.Enabled = false;Calculator1.Factorial();}private void btnFactorial2_Click(object sender, System.EventArgs e){Calculator1.varFact2 = int.Parse(txtValue.Text);btnFactorial2.Enabled = false;Calculator1.FactorialMinusOne();}private void btnAddTwo_Click(object sender, System.EventArgs e){Calculator1.varAddTwo = int.Parse(txtValue.Text);btnAddTwo.Enabled = false;Calculator1.AddTwo();}private void btnRunLoops_Click(object sender, System.EventArgs e){Calculator1.varLoopValue = int.Parse(txtValue.Text);btnRunLoops.Enabled = false;// Lets the user know that a loop is runninglblRunLoops.Text = "Looping";Calculator1.RunALoop();}

  在上一步添加的代码的下方,键入以下代码以处理窗体将从 Calculator1 接收的事件:

  

protected void FactorialHandler(double Value, double Calculations)// Displays the returned value in the appropriate label.{lblFactorial1.Text = Value.ToString();// Re-enables the button so it can be used again.btnFactorial1.Enabled = true;// Updates the label that displays the total calculations performedlblTotalCalculations.Text = "TotalCalculations are " +Calculations.ToString();}protected void FactorialMinusHandler(double Value, double Calculations){lblFactorial2.Text = Value.ToString();btnFactorial2.Enabled = true;lblTotalCalculations.Text = "TotalCalculations are " +Calculations.ToString();}protected void AddTwoHandler(int Value, double Calculations){lblAddTwo.Text = Value.ToString();btnAddTwo.Enabled = true;lblTotalCalculations.Text = "TotalCalculations are " +Calculations.ToString();}protected void LoopDoneHandler(double Calculations, int Count){btnRunLoops.Enabled = true;lblRunLoops.Text = Count.ToString();lblTotalCalculations.Text = "TotalCalculations are " +Calculations.ToString();}

  在 frmCalculations 的构造函数中,紧挨在 } 之前添加下列代码,以处理窗体将从 Calculator1 接收的自定义事件:

  

Calculator1.FactorialComplete += newCalculator.FactorialCompleteHandler(this.FactorialHandler);Calculator1.FactorialMinusOneComplete += newCalculator.FactorialCompleteHandler(this.FactorialMinusHandler);Calculator1.AddTwoComplete += newCalculator.AddTwoCompleteHandler(this.AddTwoHandler);Calculator1.LoopComplete += newCalculator.LoopCompleteHandler(this.LoopDoneHandler);

  测试应用程序

  现在项目已经创建完毕,该项目将能够执行若干个复杂计算的组件与窗体结合在一起。尽管尚未实现多线程处理功能,但在继续之前应该对项目进行测试,以验证它的功能。

  测试项目

  从调试菜单中选择启动。

  应用程序启动并显示 frmCalculations。

  在文本框中键入 4,然后单击标记为添加两个的按钮。

  在按钮下方的标签中应该显示数字6,在 lblTotalCalculations 中应该显示Total Calculations are 1。

  现在单击标记为阶乘 - 1的按钮。

  该按钮的下方应显示数字6,而 lblTotalCalculations 中现在应显示Total Calculations are 4。

  将文本框中的值更改为 20,然后单击标记为阶乘的按钮。

  该按钮的下方显示数字2.43290200817664E+18,而 lblTotalCalculations 中现在显示为Total Calculations are 24。

  将文本框中的值更改为 50000,然后单击标记为运行循环的按钮。

  注意,在此按钮重新启用前有一个短暂然而明显的间隔。此按钮下的标签应显示50000,而总的计算次数显示为25000024。

  将文本框中的值更改为 5000000 并单击标记为运行循环的按钮,紧接着单击标记为添加两个的按钮。再次单击它。

  直到循环已经完成,该按钮以及窗体上的任何控件才有响应。

  如果程序只运行单个执行线程,则类似上述示例的频繁使用处理器的计算倾向于占用该程序,直到计算已经完成。在下一节中,您将向应用程序添加多线程处理功能,以便一次可以运行多个线程。

  添加多线程处理功能

  上面的示例演示了只运行单个执行线程的应用程序的限制。在下一节,您将使用 Thread 类对象向组件添加多个执行线程。

  添加 Threads 子例程

  在代码编辑器中打开 Calculator.cs。

  在代码顶部附近,找到类声明,紧接着 { 的下方,键入下列代码:

  

// Declares the variables you will use to hold your thread objects.public System.Threading.Thread FactorialThread;public System.Threading.Thread FactorialMinusOneThread;public System.Threading.Thread AddTwoThread;public System.Threading.Thread LoopThread;

  在代码底部紧接着类声明结尾之前,添加以下方法:

  

public void ChooseThreads(int threadNumber){// Determines which thread to start based on the value it receives.switch(threadNumber){case 1:// Sets the thread using the AddressOf the subroutine where// the thread will start.FactorialThread = new System.Threading.Thread(newSystem.Threading.ThreadStart(this.Factorial));// Starts the thread.FactorialThread.Start();break;case 2:FactorialMinusOneThread = newSystem.Threading.Thread(newSystem.Threading.ThreadStart(this.FactorialMinusOne));FactorialMinusOneThread.Start();break;case 3:AddTwoThread = new System.Threading.Thread(newSystem.Threading.ThreadStart(this.AddTwo));AddTwoThread.Start();break;case 4:LoopThread = new System.Threading.Thread(newSystem.Threading.ThreadStart(this.RunALoop));LoopThread.Start();break;}}

  当实例化 Thread 对象时,它要求一个 ThreadStart 对象形式的参数。ThreadStart 对象是一个指向开始线程的方法的地址的委托。ThreadStart 对象不能接受参数或者传递值,因此只能表示 void 方法。刚才实现的 ChooseThreads 方法将从调用它的程序接收一个值,并使用该值来确定要启动的适当线程。

  向 frmCalculations 添加适当的代码

  在代码编辑器中打开 frmCalculations.cs 文件,然后找到 protected void btnFactorial1_Click。

  注释掉直接调用 Calculator1.Factorial1 方法的行,如下所示:

  

// Calculator1.Factorial()

  添加下列行,以调用 Calculator1.ChooseThreads 方法:

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

  

// Passes the value 1 to Calculator1, thus directing it to start the// correct thread.Calculator1.ChooseThreads(1);

  对其他 button_click 子例程作类似的修改。

  注意 一定要为 Threads 参数包含适当的值。

  完成后,代码看起来应该类似以下形式:

  

protected void btnFactorial1_Click(object sender, System.EventArgs e)// Passes the value typed in the txtValue to Calculator.varFact1{Calculator1.varFact1 = int.Parse(txtValue.Text);// Disables the btnFactorial1 until this calculation is completebtnFactorial1.Enabled = false;// Calculator1.Factorial();Calculator1.ChooseThreads(1);}protected void btnFactorial2_Click(object sender, System.EventArgs e){Calculator1.varFact2 = int.Parse(txtValue.Text);btnFactorial2.Enabled = false;// Calculator1.FactorialMinusOne();Calculator1.ChooseThreads(2);}protected void btnAddTwo_Click(object sender, System.EventArgs e){Calculator1.varAddTwo = int.Parse(txtValue.Text);btnAddTwo.Enabled = false;// Calculator1.AddTwo();Calculator1.ChooseThreads(3);}protected void btnRunLoops_Click(object sender, System.EventArgs e){Calculator1.varLoopValue = int.Parse(txtValue.Text);btnRunLoops.Enabled = false;// Lets the user know that a loop is runninglblRunLoops.Text = "Looping";// Calculator1.RunALoop();Calculator1.ChooseThreads(4);}

  封送处理对控件的调用

  现在将加速窗体上的显示更新。鉴于控件总是由主执行线程所有,从属线程中对控件的任何调用都需要封送处理调用。封送处理是跨线程边界移动调用的行为,需要耗费大量的资源。为了使需要发生的封送处理量减到最少,并确保以线程安全方式处理调用,应使用 Control.BeginInvoke 方法来调用主执行线程上的方法,从而使必须发生的跨线程边界的封送处理量减到最少。当调用操作控件的方法时,这种调用非常必要。有关详细信息,请参见从线程操作控件。

  创建控件调用过程

  为 frmCalculations 打开代码编辑器。在声明部分,添加下列代码:

  

public delegate void FHandler(double Value, double Calculations);public delegate void A2Handler(int Value, double Calculations);public delegate void LDHandler(double Calculations, int Count);

  Invoke 和 BeginInvoke 需要将适当方法的委托作为参数。这些代码行声明一些委托签名,这些签名将被 BeginInvoke 用于调用适当的方法。

  在代码中添加下列空方法。

  

public void FactHandler(double Value, double Calculations){}public void Fact1Handler(double Value, double Calculations){}public void Add2Handler(int Value, double Calculations){}public void LDoneHandler(double Calculations, int Count){}

  在编辑菜单中,使用剪切和粘贴,从 FactorialHandler 方法中剪切所有代码,并将其粘贴到 FactHandler 中。

  对 FactorialMinusHandler 和 Fact1Handler、AddTwoHandler 和 Add2Handler 以及 LoopDoneHandler 和 LDoneHandler 重复上面的步骤。

  完成后,在 FactorialHandler、Factorial1Handler、AddTwoHandler 和 LoopDoneHandler 中应该没有剩余代码,并且它们曾经包含的所有代码应该已经移动到适当的新方法中。

  调用 BeginInvoke 方法以异步调用这些方法。可以从窗体 (this) 或者窗体上的任何控件调用 BeginInvoke。

  完成后,代码看起来应该类似以下形式:

  

protected void FactorialHandler(double Value, double Calculations){// BeginInvoke causes asynchronous execution to begin at the address// specified by the delegate. Simply put, it transfers execution of// this method back to the main thread. Any parameters required by// the method contained at the delegate are wrapped in an object and// passed.this.BeginInvoke(new FHandler(FactHandler), new Object[]{Value, Calculations});}protected void FactorialMinusHandler(double Value, double Calculations){this.BeginInvoke(new FHandler(Fact1Handler), new Object []{Value, Calculations});}protected void AddTwoHandler(int Value, double Calculations){this.BeginInvoke(new A2Handler(Add2Handler), new Object[]{Value, Calculations});}protected void LoopDoneHandler(double Calculations, int Count){this.BeginInvoke(new LDHandler(LDoneHandler), new Object[]{Calculations, Count});}

来源:http://www.tulaoshi.com/n/20160219/1611526.html

延伸阅读
Thread 创建线程的两种方法: 1、定义类继承Thread类,覆写类中的run方法,调用类对象的start方法,start方法启动线程,调用run方法。Thread类用于描述线程;该类定义一个功能run,用于存储线程要运行的代码。 2、定义类实现Runnable接口,覆盖Runnable接口中的方法,通过Thread类建立线程对象,将Runnable接口的子类对象作为实际参数传递给T...
点此下载本文源代码 现在人们具备一台数码相机已经不是什么新鲜事了,更何况500万像素以上的数码相机更是逐渐成了主流。相比较以前以胶卷为感光介质的普通相机,数码相机可以将所照图像即刻转换成计算机可识别的图像文件格式以便浏览、共享和打印。虽然数码相机在技术和方便性上都远远高于普通相机,但是笔者发现所有已经生成的图像文...
我这里可以大概给你介绍一下,但对于每一种编程模型要看具体的示例是什么,而且我不可能给你罗列所有的代码,请谅解。 其实我们编程只要尽量站到比较高的层次,很多道理其实你会发现你已经懂了。 就多线程来说,我们开始设想只有两个线程(2时是不是算数学归纳法?)那么如果两个独立的线程会发生什么呢? 1。当一个线程进...
随着信息时代的到来,需要处理的信息量越来越庞大,需要解决的问题越来越复杂,使得计算量剧增。通过提高单个处理器的计算速度和采用传统的"顺序(串行)"计算技术已难以胜任。因此,需要有功能更强大的计算机系统和计算机技术来支撑。并行计算机及并行计算技术应运而生。     但由于缺乏实验环境和机...
1.多线程中有主内存和工作内存之分, 在JVM中,有一个主内存,专门负责所有线程共享数据;而每个线程都有他自己私有的工作内存, 主内存和工作内存分贝在JVM的stack区和heap区。 2.线程的状态有'Ready', 'Running', 'Sleeping', 'Blocked', 和 'Waiting&...

经验教程

605

收藏

19
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部