多线程编程(4):多线程与UI操作

简介:
为了让程序尽快响应用户操作,在开发Windows应用程序时经常会使用到线程。对于耗时的操作如果不使用线程将会是UI界面长时间处于停滞状态,这种情况是用户非常不愿意看到的,在这种情况下我们希望使用线程来解决这个问题。
下面是一个使用多线程操作界面UI的代码:
using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Text; 
using System.Windows.Forms; 
using System.Threading; 
 
namespace ThreadPoolDemo 

         public partial  class ThreadForm : Form 
        { 
                 public ThreadForm() 
                { 
                        InitializeComponent(); 
                } 
 
                 private  void btnThread_Click( object sender, EventArgs e) 
                { 
                        Thread thread =  new Thread( new ThreadStart(Run)); 
                        thread.Start(); 
                } 
 
                 private  void Run() 
                { 
                         while (progressBar.Value < progressBar.Maximum) 
                        { 
                                progressBar.PerformStep(); 
                        } 
                } 
        } 


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

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

         public partial  class ThreadForm : Form 
        { 
                 public ThreadForm() 
                { 
                        InitializeComponent(); 
                } 
 
                 private  void btnThread_Click( object sender, EventArgs e) 
                { 
                        Thread thread =  new Thread( new ThreadStart(Run)); 
                        thread.Start(); 
                } 
 
                 private  void Run() 
                { 
                         while (progressBar.Value < progressBar.Maximum) 
                        { 
                                progressBar.PerformStep(); 
                        } 
                } 
        } 


这样再执行程序就不会抛出异常了。
不过使用上面的代码我们可能还有些犯嘀咕,毕竟是不允许直接在线程中直接操作界面的,那么我们还可以用Invoke方法。

Invoke方法来操作界面
下面是一个例子:
using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Threading; 
 
namespace ThreadPoolDemo 

         public partial  class ThreadForm : Form 
        { 
                 //定义delegate以便Invoke时使用 
                 private  delegate  void SetProgressBarValue( int value); 
                 public ThreadForm() 
                { 
                        InitializeComponent(); 
                } 
 
                 private  void btnThread_Click( object sender, EventArgs e) 
                { 
                        progressBar.Value = 0; 
                         //指示是否对错误线程的调用,即是否允许在创建UI的线程之外访问线程 
                         //CheckForIllegalCrossThreadCalls = false; 
                        Thread thread =  new Thread( new ThreadStart(Run)); 
                        thread.Start(); 
                } 
                 //使用线程来直接设置进度条 
                 private  void Run() 
                { 
                         while (progressBar.Value < progressBar.Maximum) 
                        { 
                                progressBar.PerformStep(); 
                        } 
                } 
 
                 private  void btnInvoke_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.Value = value; 
                } 
        } 

 

这个方法的功能跟上面的操作是一样的,只不过不需要设置CheckForIllegalCrossThreadCalls属性,而且还不会抛出异常,当然除了上面的方法之外,还可以使用BackgroundWorker类来完成同样的功能。

BackgroundWorker类操作界面
因为使用BackgroundWorker类操作UI界面的例子周公博客上已经有过例子,所以这里的例子代码注释比较简单,读者可以看周公以前的示例,这次所使用的代码示例如下:
using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Threading; 
 
namespace ThreadPoolDemo 

         public partial  class ThreadForm : Form 
        { 
                 //定义delegate以便Invoke时使用 
                 private  delegate  void SetProgressBarValue( int value); 
                 public ThreadForm() 
                { 
                        InitializeComponent(); 
                } 
 
                 private  void btnThread_Click( object sender, EventArgs e) 
                { 
                        progressBar.Value = 0; 
                         //指示是否对错误线程的调用,即是否允许在创建UI的线程之外访问线程 
                         //CheckForIllegalCrossThreadCalls = false; 
                        Thread thread =  new Thread( new ThreadStart(Run)); 
                        thread.Start(); 
                } 
                 //使用线程来直接设置进度条 
                 private  void Run() 
                { 
                         while (progressBar.Value < progressBar.Maximum) 
                        { 
                                progressBar.PerformStep(); 
                        } 
                } 
 
                 private  void btnInvoke_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.Value = value; 
                } 
        } 

 

 当然,除了BackgroundWorker可以完成上面的功能之外,利用System.Windows.Forms.Timer类也能完场上面的功能,代码如下:
using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Threading; 
 
namespace ThreadPoolDemo 

         public partial  class ThreadForm : Form 
        { 
                 //定义delegate以便Invoke时使用 
                 private  delegate  void SetProgressBarValue( int value); 
                 private BackgroundWorker worker; 
                 public ThreadForm() 
                { 
                        InitializeComponent(); 
                } 
 
                 private  void btnThread_Click( object sender, EventArgs e) 
                { 
                        progressBar.Value = 0; 
                         //指示是否对错误线程的调用,即是否允许在创建UI的线程之外访问线程 
                         //CheckForIllegalCrossThreadCalls = false; 
                        Thread thread =  new Thread( new ThreadStart(Run)); 
                        thread.Start(); 
                } 
                 //使用线程来直接设置进度条 
                 private  void Run() 
                { 
                         while (progressBar.Value < progressBar.Maximum) 
                        { 
                                progressBar.PerformStep(); 
                        } 
                } 
 
                 private  void btnInvoke_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.Value = value; 
                } 
 
                 private  void btnBackgroundWorker_Click( object sender, EventArgs e) 
                { 
                        progressBar.Value = 0; 
                        worker =  new BackgroundWorker(); 
                        worker.DoWork +=  new DoWorkEventHandler(worker_DoWork); 
                         //当工作进度发生变化时执行的事件处理方法 
                        worker.ProgressChanged +=  new ProgressChangedEventHandler(worker_ProgressChanged); 
                         //当事件处理完毕后执行的方法 
                        worker.RunWorkerCompleted +=  new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
                        worker.WorkerReportsProgress =  true; //支持报告进度更新 
                        worker.WorkerSupportsCancellation =  false; //不支持异步取消 
                        worker.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) 
                        { 
                                worker.ReportProgress(++value); //汇报进度 
                        } 
                } 
                 //使用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; 
                        } 
                } 
        } 

 
总结:本篇主要讲述了使用线程操作Windows应用程序界面的方法,这些方法在编写多线程的UI程序时可以参考。


















本文转自周金桥51CTO博客,原文链接: http://blog.51cto.com/zhoufoxcn/267495 ,如需转载请自行联系原作者

相关文章
|
17天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
101 2
|
17天前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
47 1
|
2月前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
1月前
|
缓存 Java 调度
多线程编程核心:上下文切换深度解析
在现代计算机系统中,多线程编程已成为提高程序性能和响应速度的关键技术。然而,多线程编程中一个不可避免的概念就是上下文切换(Context Switching)。本文将深入探讨上下文切换的概念、原因、影响以及优化策略,帮助你在工作和学习中深入理解这一技术干货。
50 10
|
2月前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
1月前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
1月前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
53 3
|
1月前
|
算法 调度 开发者
多线程编程核心:上下文切换深度解析
在多线程编程中,上下文切换是一个至关重要的概念,它直接影响到程序的性能和响应速度。本文将深入探讨上下文切换的含义、原因、影响以及如何优化,帮助你在工作和学习中更好地理解和应用多线程技术。
44 4
|
15天前
|
安全 Java API
【JavaEE】多线程编程引入——认识Thread类
Thread类,Thread中的run方法,在编程中怎么调度多线程
|
2月前
|
安全 Java 开发者
Java中的多线程编程:从基础到实践
本文深入探讨了Java多线程编程的核心概念和实践技巧,旨在帮助读者理解多线程的工作原理,掌握线程的创建、管理和同步机制。通过具体示例和最佳实践,本文展示了如何在Java应用中有效地利用多线程技术,提高程序性能和响应速度。
72 1
下一篇
开通oss服务