BackgroundWorker 组件概述。
在本演练中,将创建一个简单的多线程组件,该组件可以同时执行若干个复杂计算。
另外还要在组件中实现一些方法,根据这些变量的值执行计算。
注意 |
---|
在本演示中,您将通过更新公共变量将值返回到用户界面,当线程执行完毕后,使用事件通知主程序。 使用设置。 |
创建窗体
-
“Windows 应用程序”项目。
-
“是”。
该窗体将用作应用程序的主用户界面。
-
TextBox 控件。
-
为这些控件设置属性,如下所示:
控件
名称
文本
label1
lblFactorial1
(空)
label2
lblFactorial2
(空)
label3
lblAddTwo
(空)
label4
lblRunLoops
(空)
label5
lblTotalCalculations
(空)
button1
btnFactorial1
Factorial
button2
btnFactorial2
Factorial - 1
button3
btnAddTwo
Add Two
button4
btnRunLoops
Run a Loop
textBox1
txtValue
(空)
创建 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); }
将用户输入传输到组件
Calculator 组件接收值和向它传送值。
实现 frmCalculations 的前端功能
-
frmCalculations。
-
{ 的下方键入:
Calculator Calculator1;
-
} 的前面,添加下面的代码行:
// Creates a new instance of Calculator. Calculator1 = new Calculator();
-
Click 事件处理程序生成代码大纲,并添加创建这些处理程序的代码。
Click 事件处理程序应如下所示:
// Passes the value typed in the txtValue to Calculator.varFact1. private void btnFactorial1_Click(object sender, System.EventArgs e) { 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 running lblRunLoops.Text = "Looping"; Calculator1.RunALoop(); }
-
Calculator1 接收的事件:
private 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 performed lblTotalCalculations.Text = "TotalCalculations are " + Calculations.ToString(); } private void FactorialMinusHandler(double Value, double Calculations) { lblFactorial2.Text = Value.ToString(); btnFactorial2.Enabled = true; lblTotalCalculations.Text = "TotalCalculations are " + Calculations.ToString(); } private void AddTwoHandler(int Value, double Calculations) { lblAddTwo.Text = Value.ToString(); btnAddTwo.Enabled = true; lblTotalCalculations.Text = "TotalCalculations are " + Calculations.ToString(); } private void LoopDoneHandler(double Calculations, int Count) { btnRunLoops.Enabled = true; lblRunLoops.Text = Count.ToString(); lblTotalCalculations.Text = "TotalCalculations are " + Calculations.ToString(); }
-
Calculator1 接收的自定义事件。
Calculator1.FactorialComplete += new Calculator.FactorialCompleteHandler(this.FactorialHandler); Calculator1.FactorialMinusOneComplete += new Calculator.FactorialCompleteHandler(this.FactorialMinusHandler); Calculator1.AddTwoComplete += new Calculator.AddTwoCompleteHandler(this.AddTwoHandler); Calculator1.LoopComplete += new Calculator.LoopCompleteHandler(this.LoopDoneHandler);
测试应用程序
尽管尚未实现多线程处理功能,但在继续之前应该对项目进行测试,以验证它的功能。
测试项目
-
“启动调试”。
frmCalculations。
-
“Add Two”的按钮。
lblTotalCalculations 中应该显示“Total Calculations are 1”。
-
“Factorial - 1”的按钮。
lblTotalCalculations 中现在应显示“Total Calculations are 4”。
-
“Factorial”的按钮。
lblTotalCalculations 中现在显示为“Total Calculations are 24”。
-
“Run A Loop”的按钮。
此按钮下的标签应显示“50000”,而总的计算次数显示为“25000024”。
-
再次单击它。
直到循环已经完成,该按钮以及窗体上的任何控件才有响应。
在下一节中,您将向应用程序添加多线程处理功能,以便一次可以运行多个线程。
添加多线程处理功能
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(new System.Threading.ThreadStart(this.Factorial)); // Starts the thread. FactorialThread.Start(); break; case 2: FactorialMinusOneThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.FactorialMinusOne)); FactorialMinusOneThread.Start(); break; case 3: AddTwoThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.AddTwo)); AddTwoThread.Start(); break; case 4: LoopThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.RunALoop)); LoopThread.Start(); break; } }
ChooseThreads 方法将从调用它的程序接收值,并使用该值来确定要启动的适当线程。
向 frmCalculations 添加适当的代码
-
private void btnFactorial1_Click。
-
Calculator1.Factorial1 方法的行,如下所示:
// Calculator1.Factorial()
-
Calculator1.ChooseThreads 方法:
// Passes the value 1 to Calculator1, thus directing it to start the // correct thread. Calculator1.ChooseThreads(1);
-
-
button_click 方法进行类似的修改。
注意 Threads 参数的适当值。
完成后,代码看起来应该类似下面这样:
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(); Calculator1.ChooseThreads(1); } private void btnFactorial2_Click(object sender, System.EventArgs e) { Calculator1.varFact2 = int.Parse(txtValue.Text); btnFactorial2.Enabled = false; // Calculator1.FactorialMinusOne(); Calculator1.ChooseThreads(2); } private void btnAddTwo_Click(object sender, System.EventArgs e) { Calculator1.varAddTwo = int.Parse(txtValue.Text); btnAddTwo.Enabled = false; // Calculator1.AddTwo(); Calculator1.ChooseThreads(3); } 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 running lblRunLoops.Text = "Looping"; // Calculator1.RunALoop(); Calculator1.ChooseThreads(4); }
封送对控件的调用
如何:从线程中操作控件。
创建控件调用过程
-
在声明部分,添加下列代码:
public delegate void FHandler(double Value, double Calculations); public delegate void A2Handler(int Value, double Calculations); public delegate void LDHandler(double Calculations, int Count);
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) { }
-
FactHandler 中。
-
LDoneHandler 重复上面的步骤。
LoopDoneHandler 中应该没有剩余的代码,它们以前包含的所有代码都应该已经移到适当的新方法中。
-
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}); }
如何:从线程中操作控件。
-
保存您的工作。
-
“启动调试”来测试您的解决方案。
-
“Run A Loop”。
如果它完成得太快,请相应地调整该数字的大小。
-
“Run A Loop”按钮的下方。
用户界面保持对输入的响应,并在每个线程完成后返回结果。
-
协调线程
Calculator 中每个执行计算的方法撤回以下代码行:
varTotalCalculations += 1; varTotalAsOfNow = varTotalCalculations;
lock 的语法如下所示:
lock(AnObject) { // Insert code that affects the object. // Insert more code that affects the object. // Insert more code that affects the object. // Release the lock. }
} 时,表达式将被释放,线程将可以继续正常工作。
将 lock 语句添加到应用程序
-
“Calculator.cs”。
-
找到下列代码的每个实例:
varTotalCalculations += 1; varTotalAsOfNow = varTotalCalculations;
应该有此代码的四个实例,每个计算方法中有一个。
-
修改此代码,使其显示为如下形式:
lock(this) { varTotalCalculations += 1; varTotalAsOfNow = varTotalCalculations; }
-
保存工作,并按上例所示进行测试。
应该认真考虑锁定线程的必要性,并且仅当绝对必要时才予以实现。
请发表评论