• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

演练:使用VS2010C#创作简单的多线程组件

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

BackgroundWorker 组件概述。

在本演练中,将创建一个简单的多线程组件,该组件可以同时执行若干个复杂计算。


另外还要在组件中实现一些方法,根据这些变量的值执行计算。

注意

在本演示中,您将通过更新公共变量将值返回到用户界面,当线程执行完毕后,使用事件通知主程序。

使用设置

创建窗体

  1. “Windows 应用程序”项目。

  2. “是”

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

  3. TextBox 控件。

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

     

    控件

    名称

    文本

    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 组件

  1. “添加组件”

  2. Calculator

向 Calculator 组件添加公共变量

  1. “代码编辑器”

  2. frmCalculations 传递给每个线程。

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

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

向 Calculator 组件添加方法和事件

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

    注意

    尽管您将声明 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);
    
  2. 为实现此目的,紧接着上一步输入的代码的下方,添加下列代码。

    public event FactorialCompleteHandler FactorialComplete;
    public event FactorialCompleteHandler FactorialMinusOneComplete;
    public event AddTwoCompleteHandler AddTwoComplete;
    public event LoopCompleteHandler LoopComplete;
    
  3. 紧接着上一步键入的代码的下方,键入下列代码:

    // 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 的前端功能

  1. frmCalculations

  2. { 的下方键入:

    Calculator Calculator1;
    
  3. } 的前面,添加下面的代码行:

    // Creates a new instance of Calculator.
    Calculator1 = new Calculator();
    
  4. 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();
    }
    
  5. 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();
    }
    
  6. 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);
    

测试应用程序


尽管尚未实现多线程处理功能,但在继续之前应该对项目进行测试,以验证它的功能。

测试项目

  1. “启动调试”

    frmCalculations

  2. “Add Two”的按钮。

    lblTotalCalculations 中应该显示“Total Calculations are 1”。

  3. “Factorial - 1”的按钮。

    lblTotalCalculations 中现在应显示“Total Calculations are 4”。

  4. “Factorial”的按钮。

    lblTotalCalculations 中现在显示为“Total Calculations are 24”。

  5. “Run A Loop”的按钮。

    此按钮下的标签应显示“50000”,而总的计算次数显示为“25000024”。

  6. 再次单击它。

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

    在下一节中,您将向应用程序添加多线程处理功能,以便一次可以运行多个线程。

添加多线程处理功能


Thread 类向组件添加多个执行线程。

添加 Threads 子例程

  1. “Calculator.cs”

  2. { 的下方,键入以下代码:

    // 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;
    
  3. 在代码底部紧接着类声明结尾之前,添加以下方法:

    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 添加适当的代码

  1. private void btnFactorial1_Click

    1. Calculator1.Factorial1 方法的行,如下所示:

      // Calculator1.Factorial()
      
    2. Calculator1.ChooseThreads 方法:

      // Passes the value 1 to Calculator1, thus directing it to start the 
      // correct thread.
      Calculator1.ChooseThreads(1);
      
  2. 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);
    }
    

封送对控件的调用

 

如何:从线程中操作控件

创建控件调用过程

  1. 在声明部分,添加下列代码:

    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 用来调用适当的方法。

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

    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)
    {
    }
    
  3. FactHandler 中。

  4. LDoneHandler 重复上面的步骤。

    LoopDoneHandler 中应该没有剩余的代码,它们以前包含的所有代码都应该已经移到适当的新方法中。

  5. 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});
    }
    

    如何:从线程中操作控件

  6. 保存您的工作。

  7. “启动调试”来测试您的解决方案。

    1. “Run A Loop”

      如果它完成得太快,请相应地调整该数字的大小。

    2. “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 语句添加到应用程序

  1. “Calculator.cs”

  2. 找到下列代码的每个实例:

    varTotalCalculations += 1;
    varTotalAsOfNow = varTotalCalculations;
    

    应该有此代码的四个实例,每个计算方法中有一个。

  3. 修改此代码,使其显示为如下形式:

    lock(this)
    {
       varTotalCalculations += 1;
       varTotalAsOfNow = varTotalCalculations;
    }
    
  4. 保存工作,并按上例所示进行测试。

    应该认真考虑锁定线程的必要性,并且仅当绝对必要时才予以实现。


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C#同步/并发队列ConcurrentQueue发布时间:2022-07-14
下一篇:
java代码转换为c#工具发布时间:2022-07-14
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap