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

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Text;
  7. using System.Windows.Forms;
  8. using System.Threading;
  9. namespace ThreadPoolDemo
  10. {
  11. public partial class ThreadForm : Form
  12. {
  13. public ThreadForm()
  14. {
  15. InitializeComponent();
  16. }
  17. private void btnThread_Click(object sender, EventArgs e)
  18. {
  19. Thread thread = new Thread(new ThreadStart(Run));
  20. thread.Start();
  21. }
  22. private void Run()
  23. {
  24. while (progressBar.Value < progressBar.Maximum)
  25. {
  26. progressBar.PerformStep();
  27. }
  28. }
  29. }
  30. }

我们的本意是点击“启动”按钮来启动模拟一个操作,在进度条中显示操作的总体进度。不过如果我们真的点击“启动”按钮会很失望,因为它会抛出一个System.InvalidOperationException异常,异常描述就是“线程间操作无效: 从不是创建控件‘progressBar’的线程访问它。”
CheckForIllegalCrossThreadCalls属性
之所以会出现这样的情况是因为在.NET中做了限制,不允许在调试环境下使用线程访问并非它自己创建的UI控件,这么做可能是怕在多线程环境下对界面控件进行操作会出现不可预知的情况,如果开发者可以确认自己的代码操作界面不会出现问题,可以用比较简单的方法解决,那就是设置CheckForIllegalCrossThreadCalls这个静态属性,它默认是true,如果将其设为false的话,以后在多线程环境下操作界面也不会抛出异常了,我们上面的代码可以修改为:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Windows.Forms;
  9. using System.Threading;
  10. namespace ThreadPoolDemo
  11. {
  12. public partial class ThreadForm : Form
  13. {
  14. public ThreadForm()
  15. {
  16. InitializeComponent();
  17. }
  18. private void btnThread_Click(object sender, EventArgs e)
  19. {
  20. //指示是否对错误线程的调用,即是否允许在创建UI的线程之外访问线程
  21. CheckForIllegalCrossThreadCalls = false;
  22. Thread thread = new Thread(new ThreadStart(Run));
  23. thread.Start();
  24. }
  25. private void Run()
  26. {
  27. while (progressBar.Value < progressBar.Maximum)
  28. {
  29. progressBar.PerformStep();
  30. }
  31. }
  32. }
  33. }

这样再执行程序就不会抛出异常了。

不过使用上面的代码我们可能还有些犯嘀咕,毕竟是不允许直接在线程中直接操作界面的,那么我们还可以用Invoke方法。

Invoke方法来操作界面
下面是一个例子:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Windows.Forms;
  9. using System.Threading;
  10. namespace ThreadPoolDemo
  11. {
  12. public partial class ThreadForm : Form
  13. {
  14. //定义delegate以便Invoke时使用
  15. private delegate void SetProgressBarValue(int value);
  16. public ThreadForm()
  17. {
  18. InitializeComponent();
  19. }
  20. private void btnThread_Click(object sender, EventArgs e)
  21. {
  22. progressBar.Value = 0;
  23. //指示是否对错误线程的调用,即是否允许在创建UI的线程之外访问线程
  24. //CheckForIllegalCrossThreadCalls = false;
  25. Thread thread = new Thread(new ThreadStart(Run));
  26. thread.Start();
  27. }
  28. //使用线程来直接设置进度条
  29. private void Run()
  30. {
  31. while (progressBar.Value < progressBar.Maximum)
  32. {
  33. progressBar.PerformStep();
  34. }
  35. }
  36. private void btnInvoke_Click(object sender, EventArgs e)
  37. {
  38. progressBar.Value = 0;
  39. Thread thread = new Thread(new ThreadStart(RunWithInvoke));
  40. thread.Start();
  41. }
  42. //使用Invoke方法来设置进度条
  43. private void RunWithInvoke()
  44. {
  45. int value = progressBar.Value;
  46. while (value< progressBar.Maximum)
  47. {
  48. //如果是跨线程调用
  49. if (InvokeRequired)
  50. {
  51. this.Invoke(new SetProgressBarValue(SetProgressValue), value++);
  52. }
  53. else
  54. {
  55. progressBar.Value = ++value;
  56. }
  57. }
  58. }
  59. //跟SetProgressBarValue委托相匹配的方法
  60. private void SetProgressValue(int value)
  61. {
  62. progressBar.Value = value;
  63. }
  64. }
  65. }

这个方法的功能跟上面的操作是一样的,只不过不需要设置CheckForIllegalCrossThreadCalls属性,而且还不会抛出异常。

多线程与UI操作(二)的更多相关文章

  1. C# Wpf异步修改UI,多线程修改UI(二)

    1.使用定时器异步修改 这是相对比较简单的方法 在Wpf中定时器使用DiapatcherTimer,不使用Timer原因: 在一个应用程序中,Timer会重复生成time事件,而DispatcherT ...

  2. 多线程与UI操作(一)

    C#中禁止跨线程直接访问控件,InvokeRequired是为了解决这个问题而产生的,当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它. 此时它将会在内部调用n ...

  3. Android 子线程 UI 操作真的不可以?

    作者:vivo 互联网大前端团队- Zhang Xichen 一.背景及问题 某 SDK 有 PopupWindow 弹窗及动效,由于业务场景要求,对于 App 而言,SDK 的弹窗弹出时机具有随机性 ...

  4. 在多线程中进行UI操作

    那么在子线程中的UI操作如何处理呢?有两种方法: 一:在子线程,你需要进行的UI操作前添加dispatch_async函数,即可将代码块中的工作转回到主线程 dispatch_async(dispat ...

  5. 在多线程中进行UI操作--ios学习笔记

    iOS 上不建议在非主线程进行UI操作,在非主线程进行UI操作有很大几率会导致程序崩溃,或者出现预期之外的效果. 我开始不知道这一点,在子线程中进行了弹窗操作,结果程序就出问题了! 报的错误是(EXC ...

  6. Java多线程编程——进阶篇二

    一.线程的交互 a.线程交互的基础知识 线程交互知识点需要从java.lang.Object的类的三个方法来学习:    void notify()           唤醒在此对象监视器上等待的单个 ...

  7. 拒绝卡顿——在WPF中使用多线程更新UI

    原文:拒绝卡顿--在WPF中使用多线程更新UI 有经验的程序员们都知道:不能在UI线程上进行耗时操作,那样会造成界面卡顿,如下就是一个简单的示例: public partial class MainW ...

  8. Qt中如何禁掉所有UI操作以及注意事项(处理各个widget的eventFilter这一层,但是感觉不好,为什么不使用QApplication呢)

    刚做完的一个项目,在测试时出现了一个问题:由于多线程的存在,当进行语音识别时:如果用户点击程序界面上的button或者其他接受点击事件后会发出信号的widget时,程序会crash ! 后来尝试着从多 ...

  9. java 轻量级同步volatile关键字简介与可见性有序性与synchronized区别 多线程中篇(十二)

    概念 JMM规范解决了线程安全的问题,主要三个方面:原子性.可见性.有序性,借助于synchronized关键字体现,可以有效地保障线程安全(前提是你正确运用) 之前说过,这三个特性并不一定需要全部同 ...

随机推荐

  1. TextureMerger使用教程

    https://bbs.egret.com/thread-1653-1-1.html TextureMerger使用教程 2014-10-28 15:53 1862932 本帖最后由 E-Tool君 ...

  2. ojdbc15-10.2.0.4.0.jar maven 引用报错 Dependency 'com.oracle:ojdbc15:10.2.0.4.0' not found

    ojdbc15-10.2.0.4.0.jar maven 引用报错 问题现象 在 Maven 工程中引用 ojdbc15-10.2.0.4.0.jar 报错,报错信息:Dependency 'com. ...

  3. RESTful 介绍

    什么是RESTful?一种软件架构风格.设计风格,而不是标准,只是提供了一组设计原则和约束条件.它主要用于客户端和服务端交互类的软件.基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制. ...

  4. gulp4.0配置

    var gulp = require('gulp'); var rename = require('gulp-rename');//重命名 var uglify=require('gulp-uglif ...

  5. python3.4 + pycharm安装与使用

    因个人是windows的环境,所以本文只讲windows环境下的python安装. 作为初用python的盆友,强烈建议只在电脑上装一个python版本就好了,不然就进了各种坑里了. Python安装 ...

  6. LeetCode.1128-等价多米诺骨牌对的数量(Number of Equivalent Domino Pairs)

    这是小川的第394次更新,第428篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第259题(顺位题号是1128).给定多米诺骨牌列表,当且仅当(a == c且b == d ...

  7. PJzhang:shell基础入门的2个疗程-two

    猫宁!!! 第6节:重定向 标准输入,标准输出,错误输出 输入重定向符号'<' 输出重定向符号'>'(清空之后再输入),'>>'(当前内容不变,在最后一行追加),'2>' ...

  8. Codeforces Round #594 (Div. 2)(A/B/C)

    A. Integer PointsDescription DLS and JLS are bored with a Math lesson. In order to entertain themsel ...

  9. C学习笔记-结构体与二进制文件增删改查

    使用结构体整理数据,然后利用二进制存储文件,这样存储的文件类似于数据库,可以实现文件的增删改查 定义结构体 struct student { unsigned int ID; char name[20 ...

  10. python中super函数的参考

    https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ http://wiki.jikexueyuan.com/proj ...