• 多线程下访问控件的方式


    多线程下访问控件的方式(您一定会用到,附源码啦!)

    前言

         在很多情况下,我们都会使用到多线程,在使用多线程的时候,我们很多时候又会去访问控件,这里面就会出现很多问题!!!我以一个最常见的,我们经常会用到的例子来讲讲,在提高自己水平的同时,希望能给大家带来一些方便,有不对的地方还请留言告知,以便及时更正自己的错误思想,先谢谢啦!

    1、多线程使用

        有时候在执行一段程序时会耗时比较长,这时候会发现,我们的窗体就拖不动了,就像卡死了,但那段耗时程序执行完之后,就恢复OK了,这就是我们常说的程序假死!代码如下:

     View Code
     private void btnCalculate_Click(object sender, EventArgs e)
            {
                Calculate();
            }
            private void Calculate()
            {
                for (int i = 0; i < 500; i++)
                {
                    Thread.Sleep(5);
                }
            }

     这时候使用多线程就很容易解决程序假死问题,修改代码如下:

     View Code
     private void btnCalculate_Click(object sender, EventArgs e)
            {
                Thread mythread = new Thread(Calculate);
                mythread.IsBackground = true;        //設置為後臺線程,程式關閉后進程也關閉,如果不設置true,則程式關閉,此線程還在內存,不會關閉
                mythread.Start();
            }
            private void Calculate()
            {
                Stopwatch stopwatch = Stopwatch.StartNew();
                for (int i = 0; i < 500; i++)
                {
                    Thread.Sleep(5);
                }
                stopwatch.Stop();
                long lSearchTime = stopwatch.ElapsedMilliseconds;
                MessageBox.Show(lSearchTime.ToString()+"毫秒");
            }

    2、访问控件方式

    以上我们就解决了程序假死的问题,可需求又来了,我想知道计算的进度,能直观的感受到计算多少了,是不是快计算完了,这时候我们想到了进度条,这时候我们就加入进度条呗,代码如下:

     View Code
      private void btnCalculate_Click(object sender, EventArgs e)
            {
                Thread mythread = new Thread(Calculate);
                mythread.IsBackground = true;        //設置為後臺線程,程式關閉后進程也關閉,如果不設置true,則程式關閉,此線程還在內存,不會關閉
                mythread.Start();
            }
            private void Calculate()
            {
                Stopwatch stopwatch = Stopwatch.StartNew();
                progressBarWay.Maximum = 500;
                for (int i = 0; i < 500; i++)
                {
                    Thread.Sleep(5);
                    progressBarWay.Value = i;
                }
                stopwatch.Stop();
                long lSearchTime = stopwatch.ElapsedMilliseconds;
                MessageBox.Show(lSearchTime.ToString()+"毫秒");
            }

    以上代码运行的话程序报错:报错信息为:从不是创建控件控件名称 的线程访问它

    下面有两种方式来实现访问控件:

    1、不安全方式访问控件

    访问 Windows 窗体控件本质上不是线程安全的。 如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。 还可能会出现其他与线程相关的 Bug,例如争用情况和死锁。 确保以线程安全方式访问控件非常重要                            ----MSDN

       ProgressBar.CheckForIllegalCrossThreadCalls = false;            只需要加入此段代码就OK,不过不推荐使用

    2、安全方式访问控件

    下面介绍一下我通过安全方式访问ProgressBar控件实现的方法

    总结为四步:

    定義實現安全訪問控件的委託,第一步:定義帶參數委託Mydelegate,第二步:定義委託要實現的方法SetprogressBar,與委託聲明一致,第三步:聲明并實例化委託并傳入委託要實現的方法,第四步:在實際使用地方調用this.Invoke(mydele, mybar, SelectStatus.myVisible)參數1:聲明的委託名,參數2:委託變量的第一個參數:參數2:委託聲明的第二個參數,具体的代码如下:

     View Code
     public partial class SafetyWay : Form
        {
            Mydelegate mydele = null;
            ProgressBar mybar = new ProgressBar();
            public SafetyWay()
            {
                InitializeComponent();
                mydele = new Mydelegate(SetprogressBar);             //第三步:聲明并實例化委託并傳入委託要實現的方法 mydele= new Mydelegate(SetprogressBar)
            }
    
            private void btnCalculate_Click(object sender, EventArgs e)
            {
                Thread mythread = new Thread(Calculate);
                mythread.IsBackground = true;        //設置為後臺線程,程式關閉后進程也關閉,如果不設置true,則程式關閉,此線程還在內存,不會關閉
                mythread.Start();
            }
            private void Calculate()
            {
                mybar.Visible = true;
                this.Invoke(mydele, mybar, SelectStatus.myVisible);
                Stopwatch stopwatch = Stopwatch.StartNew();
                mybar.Maximum = 500;
                this.Invoke(mydele, mybar, SelectStatus.myMaximum);         //第四步:在實際使用地方調用this.Invoke(mydele, mybar, SelectStatus.myVisible)參數1:聲明的委託名,參數2:委託變量的第一個參數:參數2:委託聲明的第二個參數
                for (int i = 0; i < 500; i++)
                {
                    Thread.Sleep(5);
                    mybar.Value = i;
                    this.Invoke(mydele, mybar, SelectStatus.myValue);
                }
                mybar.Visible = false;
                this.Invoke(mydele, mybar, SelectStatus.myVisible);
                stopwatch.Stop();
                long lSearchTime = stopwatch.ElapsedMilliseconds;
                MessageBox.Show(lSearchTime.ToString() + "毫秒");
            }
            #region 通過委託要實現的方法,設置ProgressBar的屬性值
            private void SetprogressBar(ProgressBar myBar, SelectStatus status)         //第二步:定義委託要實現的方法SetprogressBar,與委託聲明一致
            {
                if (status == SelectStatus.myVisible)
                {
                    progressBarWay.Visible = myBar.Visible;
                }
                else if (status == SelectStatus.myValue)
                {
                    progressBarWay.Value = myBar.Value;
                }
                else if (status == SelectStatus.myMaximum)
                {
                    progressBarWay.Maximum = myBar.Maximum;
                }
            }     
            #endregion
        }
        #region 定義枚舉,記錄要改變的屬性,實際意義如name所示
        public enum SelectStatus
        {
            myVisible,
            myValue,
            myMaximum
        }
        #endregion
        public delegate void Mydelegate(ProgressBar myBar, SelectStatus status); //定義實現安全訪問控件的委託,第一步:定義帶參數委託Mydelegate

    以上的代码实现方式相对繁琐,下面给出使用lambda表达式的方式来实现会发现代码非常简单,代码如下:

    复制代码
            private void btnCalculate_Click(object sender, EventArgs e)
            {
                Thread mythread = new Thread(Calculate);
                mythread.IsBackground = true;        //設置為後臺線程,程式關閉后進程也關閉,如果不設置true,則程式關閉,此線程還在內存,不會關閉
                mythread.Start();
            }
            private void Calculate()
            {
                this.Invoke(new Action(() => { progressBarWay.Visible = true; }));
                Stopwatch stopwatch = Stopwatch.StartNew();
                this.Invoke(new Action(() => { progressBarWay.Maximum = 500; }));
                for (int i = 0; i < 500; i++)
                {
                    Thread.Sleep(5);
                    this.Invoke(new Action(() => { progressBarWay.Value = i; }));
                }
                this.Invoke(new Action(() => { progressBarWay.Visible = false; }));
                stopwatch.Stop();
                long lSearchTime = stopwatch.ElapsedMilliseconds;
                MessageBox.Show(lSearchTime.ToString() + "毫秒");
            }
    复制代码

     上面的代码样子好像变化了不少,其实在编译后编译器会为我们上面省略的一系列代码再加上去的。

    结束语

             以上就是我实际使用中总结的方法,希望对大家有所帮助,附上demo下载地址:http://files.cnblogs.com/beimeng/demo.rar

    最后再附上一个获取ArrayList  数组最大值最小值的方法:

     View Code
      ArrayList alist = new ArrayList();
                
                alist.Add(5);
                alist.Add(9);
                alist.Add(10);
                alist.Add(3);
                alist.Add(6);
                alist.Add(3);
                int[] myArray = (int[])alist.ToArray(typeof(int)); //將ArrayList轉換為固定類型的Array的方法
                Array.Sort(myArray);                                            //將Array從小到大進行排序
                int Max = myArray[myArray.Length - 1];
                int Min = myArray[0];
     
     
    分类: C#
  • 相关阅读:
    常用MySQL函数连接
    spring boot和mybatis的多源配置亲测可用非常简单哦铁闸门
    RestTemplate请求发送post请求携带数组参数(亲测有效)
    idea配置tomcat
    前后端分离生成手机验证码
    前后端分离的图片验证保存及发送方式
    Java—大文件分片上传
    SpringAOP实战
    Spring Boot AOP的使用
    iOS开发日常记录
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3169777.html
Copyright © 2020-2023  润新知