zoukankan      html  css  js  c++  java
  • Application.DoEvents()和多线程

    首先将以下代码放到Button事件里面:

    private void btnStart_Click(object sender, EventArgs e)
      {
          for (int q = 0; q < 100000; q++)
          {
              textBox1.Text = q.ToString();
          }
      }

    你会发现当点击Start按钮后,循环会一直进行,此时窗体会出现假死的状态,如:无法拖动。直到循环结束,textBox1中才会显示出结果。如何解决窗体的假死状态??

    修改以上代码如下:

    private void btnStart_Click(object sender, EventArgs e)
      {
          for (int q = 0; q < 100000; q++)
          {
              textBox1.Text = q.ToString();
              //实时响应文本框中的值
              Application.DoEvents();
          }
      }

    此时再次点击Start按钮后,textBox中的数字会不断改变,同时,你也可以拖动窗体。。。

    但是这样使用Application.DoEvents()好吗?如果用多线程来实现相同的效果呢?

    多线程实现代码如下: 

    public Form1()
      {
          InitializeComponent();
          //不捕获跨线程调用引起的异常
          CheckForIllegalCrossThreadCalls = false;
      }
      private void btnStart_Click(object sender, EventArgs e)
      {
              Thread s1 = new Thread(new ThreadStart(ThreadMeth));
              s1.Start();
      }
      public void ThreadMeth()
      {
          for (int q = 0; q < 100000; q++)
          {

              textBox1.Text = q.ToString();
          }
      }

    注意Form1()里面添加了一行代码,解决跨线程调用产生的异常。

    以上代码和使用Application.DoEvents()达到相同的效果。

    下面我们来比较一下,使用Application.DoEvents()和使用多线程哪个更耗时?

    代码如下:
      public void ThreadMeth()
      {
          label1.Text = DateTime.Now.ToString();
          for (int q = 0; q < 100000; q++)
          {
              textBox1.Text = q.ToString();
          }
          label5.Text = DateTime.Now.ToString();

      }

      private void btnStartDo_Click(object sender, EventArgs e)
      {
          label3.Text = DateTime.Now.ToString();
          for (int q = 0; q < 100000; q++)
          {
              textBox2.Text = q.ToString();
              Application.DoEvents();//实时响应文本框中的值
          }
          label6.Text = DateTime.Now.ToString();

      }

    (假设以上比较时间的代码处在正确的位置)由此可知,Application.DoEvents()消耗的时间更少,但这里并不建议使用Application.DoEvents(),因为它会引起很多未知的错误。

    补充:由于显示的定义 CheckForIllegalCrossThreadCalls = false;并不是好的方法,下面来改进一下,如下:

     int i;

      private void btnStart_Click(object sender, EventArgs e)

      { 

           Thread s1 = new Thread(new ThreadStart(ThreadMeth)); 

           s1.Start();

      }

      private void ThreadMeth() 

      {

           for ( i = 0; i < 100000; i++)

           {

               MethodInvoker mi = new MethodInvoker(Count);

               this.Invoke(mi);

               //BeginInvoke(mi);

            }

      }

      private void Count() 

      {

           textBox1.Text = i.ToString();

      } 

    MethodInvoker实质上是一个委托,查看其定义可知...

    using System;                                                                                           

    namespace System.Windows.Forms 

    {

        // 表示一个委托,该委托可执行托管代码中声明为 void 且不接受任何参数的任何方法。

        public delegate void MethodInvoker();

    以上用匿名委托的方式似乎更简洁,代码如下:

    private void btnStart_Click(object sender, EventArgs e)                 

    {

         new Thread((ThreadStart)(delegate()

         {

               for (int i = 0; i < 10000; i++)

               {

                    label1.Invoke((MethodInvoker)delegate()

                    {

                          textBox1.Text = i.ToString();

                     });

                };

          })) .Start();                 

    在次线程上计算,在主线程上调用label。

  • 相关阅读:
    利用Trace.WriteLine定位难以重现的问题
    技术经验分享
    辞职小记
    残阳如血--读《忆秦娥·娄山关》 有感
    一个简单多线程等待窗口
    [转]Control的Invoke和BeginInvoke
    elk 改为使用 ik 中文分词器
    在 jenkins 的 pipeline 中使用分支参数
    centos8 下删除网桥 docker0
    vscode 实现组件之间的跳转
  • 原文地址:https://www.cnblogs.com/netlyf/p/4730505.html
Copyright © 2011-2022 走看看