zoukankan      html  css  js  c++  java
  • C#多线程编程(1) —— 多线程与UI操作

        为了让程序尽快响应用户操作,在开发Windows应用程序时经常会使用到线程。

        对于耗时的操作如果不使用线程将会是UI界面长时间处于停滞状态,这种情况是用户非常不愿意看到的,在这种情况下我们希望使用线程来解决这个问题。

        先看一段代码:

     1 public partial class Form1 : Form
     2     {
     3         public Form1()
     4         {
     5             InitializeComponent();
     6         }
     7 
     8         private void btnStartThread_Click(object sender, EventArgs e)
     9         {
    10             progressBar.Value = 0;
    11             Thread thread = new Thread(new ThreadStart(Run));
    12             thread.Start();
    13         }
    14 
    15         private void Run()
    16         {
    17             while (progressBar.Value < progressBar.Maximum)
    18             {
    19                 progressBar.PerformStep();
    20             }
    21         }
    22     }

        程序的界面中包含一个按钮和一个进度条。

        我们的本意是点击“启动”按钮来启动模拟一个操作,在进度条中显示操作的总体进度。不过如果我们真的点击“启动”按钮会很失望,因为它会抛出一个System.InvalidOperationException异常,异常描述就是“线程间操作无效: 从不是创建控件‘progressBar’的线程访问它。”

    解决方案一:CheckForIllegalCrossThreadCalls属性   

        之所以会出现这样的情况是因为在.NET中做了限制,不允许在调试环境下使用线程访问并非它自己创建的UI控件,这么做可能是怕在多线程环境下对界面控件进行操作会出现不可预知的情况,如果开发者可以确认自己的代码操作界面不会出现问题,可以用比较简单的方法解决,那就是设置CheckForIllegalCrossThreadCalls这个静态属性,它默认是true,如果将其设为false的话,以后在多线程环境下操作界面也不会抛出异常了,我们上面的代码可以修改为:

    public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void btnStartThread_Click(object sender, EventArgs e)
            {
                progressBar.Value = 0;
                CheckForIllegalCrossThreadCalls = false;  //指示是否对错误线程的调用,即是否允许在创建UI的线程之外访问线程
                Thread thread = new Thread(new ThreadStart(Run));
                thread.Start();
            }
    
            private void Run()
            {
                while (progressBar.Value < progressBar.Maximum)
                {
                    progressBar.PerformStep();
                }
            }
        }

    方案二:Invoke方法来操作界面

     public partial class Form1 : Form
        {
            //定义delegate以便Invoke时使用
            private delegate void SetProgressBarValue(int value);
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private void btnStartThread2_Click(object sender, EventArgs e)
            {
                progressBar.Value = 0;
                Thread thread = new Thread(new ThreadStart(RunWithInvoke));
                thread.Start();
            }
    
            //使用Invoke方法来设置进度条
            private void RunWithInvoke()
            {
                int value = progressBar.Value;
    
                while (value < progressBar.Maximum)
                {
                    //如果是跨线程调用
                    if (InvokeRequired)
                    {
                        this.Invoke(new SetProgressBarValue(SetProgressValue), ++value);
                    }
                    else
                    {
                        progressBar.Value = ++value;
                    }
                }
            }
    
            //跟SetProgressBarValue委托相匹配的方法
            private void SetProgressValue(int value)
            {
                //progressBar.PerformStep();
                progressBar.Value = value;
            }
        }

    解决方案三:BackgroundWorker类操作界面

    public partial class Form1 : Form
        {
            private BackgroundWorker backWorker;
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private void btnBackgroundWorker_Click(object sender, EventArgs e)
            {
                progressBar.Value = 0;
                backWorker = new BackgroundWorker();
                backWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
    
                //当工作进度发生变化时执行的事件处理方法
                backWorker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
    
                //当事件处理完毕后执行的方法
                backWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
                backWorker.WorkerReportsProgress = true;//支持报告进度更新
                backWorker.WorkerSupportsCancellation = false;//不支持异步取消
                backWorker.RunWorkerAsync();//启动执行
    
                btnBackgroundWorker.Enabled = false;
            }
    
            //当事件处理完毕后执行的方法
            void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                btnBackgroundWorker.Enabled = true;
            }
    
            //当工作进度发生变化时执行的事件处理方法
            void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                //可以在这个方法中与界面进行通讯
                progressBar.Value = e.ProgressPercentage;
            }
    
            //开始启动工作时执行的事件处理方法
            void worker_DoWork(object sender, DoWorkEventArgs e)
            {
                int value = progressBar.Value;
    
                while (value < progressBar.Maximum)
                {
                    backWorker.ReportProgress(++value);//汇报进度
                }
            }
        }

    解决方案四:Timer方式

    public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            //使用System.Windows.Forms.Timer来操作界面能
            private void btnTimer_Click(object sender, EventArgs e)
            {
                progressBar.Value = 0;
    
                //注意在.net中有多个命名空间下存在Timer类,为了便于区别,使用了带命名空间形式
                System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
                timer.Interval = 1;
                timer.Tick += new EventHandler(timer_Tick);
                timer.Enabled = true;
            }
    
            //Timer中要定期执行的方法
            void timer_Tick(object sender, EventArgs e)
            {
                int value = progressBar.Value;
    
                if (value < progressBar.Maximum)
                {
                    progressBar.Value = value + 100;
                }
            }
        }
  • 相关阅读:
    未解
    HDU 4642 Fliping game 解题报告
    HDU 4639 Hehe 解题报告
    深入浅出Node.js (11)
    JS文本框获取焦点
    深入理解 BFC
    JS 中函数名后面加与不加括号的区别
    ES6 箭头函数
    sublime 格式化代码
    <!--more-->搭建的博客设置主页内容高度
  • 原文地址:https://www.cnblogs.com/cyhv/p/3217264.html
Copyright © 2011-2022 走看看