我们先来看一段运行时会抛出 InvalidOperationException 异常的代码段:

private void btnThreadA_Click(object sender, EventArgs e)

{

    Thread thread = new Thread(ChangeTextBox);

    thread.IsBackground = true;

    thread.Start();

}

 

void ChangeTextBox()

{

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

    {

        int num = Int32.Parse(txtNum.Text);

        num++;

        txtNum.Text = num.ToString();

    }

}

 

       微软在子线程修改 UI 线程的控件值时给出的安全限制方案为:在 VS2005 或者更高版本中,只要不是在控件的创建线程(一般就是指UI主线程)上访问控件的属性就会抛出这个错误,解决方法就是利用控件提供的 Invoke 和 BeginInvoke 把调用封送回 UI 线程,也就是让控件属性修改在UI线程上执行;或者 禁用此安全限制。

       解决方案一:解除该控件上对错误线程调用的检查(谨慎使用)。

public Form2()

{

    InitializeComponent();

 

    // 解除 TextBox 对错误线程调用的检查

    // 如果要捕获了对错误线程的调用,则为 true(默认值);否则为 false

    // 对控件权限可以开放的更大 例如 Control、Form 等

    TextBox.CheckForIllegalCrossThreadCalls = false; 

}

       解决方案二:

void ChangeTextBox(string str)

{

    txtNum.Text = str;

}

 

// 增加一个委托

delegate void ChangeTextBoxEventHandler(string str);

 

// 次循环必须在子线程上运行,然后将最新值传递到 UI 线程 文本框才会即时变化

// 如果循环写在 ChangeTextBox 函数中,那么循环真实运行权会交由 UI 线程,你只会直接看见结果,看不到过程

void ThreadRun1()

{

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

    {

        int num = int.Parse(txtNum.Text);

        num++;

        

        // 使用 Invoke 方法,将函数运行劝交回给 UI 线程

        this.Invoke(new ChangeTextBoxEventHandler(ChangeTextBox), num.ToString());

    }

}

 

private void btnThreadB_Click(object sender, EventArgs e)

{

    Thread thread = new Thread(ThreadRun1);

    thread.IsBackground = true;

    thread.Start();

}

       这样已经能够达到效果,但不是微软案例推荐的写法。考虑到 ChangeTextBox 方法除了被子线程调用外,也可能被程序其它部分调用。因此,再次修改代码如下:

void ChangeTextBox(string str)

{

    // InvokeRequired 值判断当前修改文本框的请求是否有必要交由 UI 线程来完成

    // 如果为 Ture,说明次访问控件的行为来自子线程,则调用 Invoke 方法将代码执行权交给 UI 线程

    // 注意,下面实质上是进行了一次方法回调自身的行为,区别在于再次调用自身时,已经是 UI 线程在执行了

    if (this.InvokeRequired)

    {

        this.Invoke(new ChangeTextBoxEventHandler(ChangeTextBox), str);

    }

    else

    {

        txtNum.Text = str;

    }   

}

 

// 增加一个委托

delegate void ChangeTextBoxEventHandler(string str);

 

// 次循环必须在子线程上运行,然后将最新值传递到 UI 线程 文本框才会即时变化

// 如果循环写在 ChangeTextBox 函数中,那么循环真实运行权会交由 UI 线程,你只会直接看见结果,看不到过程

void ThreadRun1()

{

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

    {

        int num = int.Parse(txtNum.Text);

        num++;

        ChangeTextBox(num.ToString());

    }

}

 

private void btnThreadB_Click(object sender, EventArgs e)

{

    Thread thread = new Thread(ThreadRun1);

    thread.IsBackground = true;

    thread.Start();

}

       与 Invoke 方法相对应的还有 BeginInvoke ()、EndInvoke () 这些异步方法。无论是同步还是异步,这些方法总是会通过代理重新回到 UI 线程上执行。

       这些方法向 UI 线程的消息队列中放入一个消息,当 UI 线程处理这个消息时,就会在自己的上下文中执行传入的方法。换句话说,凡是使用 BeginInvoke 和 Invoke 调用的线程都是在UI主线程中执行的,所以即使这些方法里涉及到一些静态变量,也不用考虑加锁的问题。

 

 

ProgressBar 的异步调用

       在我们应用程序开发过程中,经常会遇到一些问题,需要使用多线程技术来加以解决。

       许多种类的应用程序都需要长时间操作,比如:执行一个打印任务,请求一个 Web Service 调用等。用户在这种情况下一般会去转移做其他事情来等待任务的完成,同时还希望随时可以监控任务的执行进度。

       为什么在我们切换应用程序后,会发生屏幕假死的现象呢?

       这是因为当你切换当前应用程序到后台再切换回前台时,系统需要在屏幕上重画整个用户界面。但是应用程序正在执行长任务,根本没有时间处理用户界面的重画,问题就会发生。如何解决问题呢?我们需要将长任务放在后台运行,把用户界面线程解放出来,因此我们需要另外一个线程。

      

       如何避免多线程的窗体资源访问的安全问题呢?其实非常简单,有两种方法:

  1. 不管线程是否是用户界面线程,对用户界面资源的访问统一由委托完成!
  2. 在每个 Windows Forms 用户界面类中都有一个 InvokeRequired 属性,它用来标识当前线程是否是来自UI线程之外的线程。检查这个属性的值可以决定是否需要进行异步调用委托。

 

情况一:

delegate void ShowProgressDelegate(int totalStep, int currentStep);

delegate void RunTaskDelegate(int seconds);

 

void ShowProgress(int totalStep, int currentStep)

{

    progressBar1.Maximum = totalStep;

    progressBar1.Value = currentStep;

}

 

void RunTask(int seconds)

{

    ShowProgressDelegate showProgress = new ShowProgressDelegate(ShowProgress);

 

    // 每 1/4 秒显示一次进度

    for (int i = 0; i < seconds * 4; i++)

    {

        Thread.Sleep(250);

        this.Invoke(showProgress, new object[] { seconds * 4, i + 1 });

    }

}

 

private void button1_Click(object sender, EventArgs e)

{

    RunTaskDelegate runTask = new RunTaskDelegate(RunTask);

 

    // 委托异步调用方式

    runTask.BeginInvoke(Convert.ToInt32(this.textBox1.Text), null, null);

}

 

情况二:

delegate void ShowProgressDelegate(int totalStep, int currentStep);

delegate void RunTaskDelegate(int seconds);

 

void ShowProgress(int totalStep, int currentStep)

{

    if (progressBar1.InvokeRequired)

    {

        ShowProgressDelegate showProgress = new ShowProgressDelegate(ShowProgress);

        this.BeginInvoke(showProgress, new object[] { totalStep, currentStep });

    }

    else

    {

        progressBar1.Maximum = totalStep;

        progressBar1.Value = currentStep;

    }

}

 

void RunTask(int seconds)

{

    // 每 1/4 秒显示一次进度

    for (int i = 0; i < seconds * 4; i++)

    {

        Thread.Sleep(250);

        ShowProgress(seconds * 4, i + 1);

    }

}

 

private void button1_Click(object sender, EventArgs e)

{

    RunTaskDelegate runTask = new RunTaskDelegate(RunTask);

 

    // 委托异步调用方式

    runTask.BeginInvoke(Convert.ToInt32(this.textBox1.Text), null, null);

}

C# 多线程详解 Part.02(UI 线程和子线程的互动、ProgressBar 的异步调用)的更多相关文章

  1. iOS开发——多线程OC篇&多线程详解

    多线程详解 前面介绍了多线程的各种方式及其使用,这里补一点关于多线程的概念及相关技巧与使用,相信前面不懂的地方看了这里之后你就对多线程基本上没有什么问题了! 1——首先ios开发多线程中必须了解的概念 ...

  2. iOS开发——GCD多线程详解

    GCD多线程详解 1. 什么是GCD Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,简单来说,GCD就是iOS一套解决多线程的机制,使用GCD能够最大限度简化多线程 ...

  3. Java 多线程详解(四)------生产者和消费者

    Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html Java 多线程详解(二)------如何创建进程和线程: ...

  4. java中多线程详解-synchronized

    一.介绍 当多个线程涉及到共享数据的时候,就会设计到线程安全的问题.非线程安全其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是“脏读”.发生脏读,就是取到的数据已经被其他的线 ...

  5. python多线程详解

    目录 python多线程详解 一.线程介绍 什么是线程 为什么要使用多线程 二.线程实现 threading模块 自定义线程 守护线程 主线程等待子线程结束 多线程共享全局变量 互斥锁 递归锁 信号量 ...

  6. C#多线程详解(一) Thread.Join()的详解

    bicabo   C#多线程详解(一) Thread.Join()的详解 什么是进程?当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程 ...

  7. Java多线程详解

    Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...

  8. windows多线程详解

    转自:http://blog.csdn.net/zhouxuguang236/article/details/7775232 在一个牛人的博客上看到了这篇文章,所以就转过来了,地址是http://bl ...

  9. [Python 多线程] 详解daemon属性值None,False,True的区别 (五)

    本文以多个例子介绍Python多线程中daemon属性值的区别. 回顾: 前面的文章简单介绍了在现代操作系统中,每一个进程都认为自己独占所有的计算机资源. 或者说线程就是独立的王国,进程间是相对独立的 ...

随机推荐

  1. nim也玩一行流,nim版的list comprehension

    nim 是一门风格类似python的静态编译型语言,官方网站:http://nim-lang.org 如果你想折腾nim的编辑环境,可以用sublime text3 +插件nimlime,notepa ...

  2. 线程的Abort方法有感

    今天看CSDN上一个很老的帖子,有个人说Thread.Abort()方法调用之后一定会抛出异常,我对这个有点疑问. 于是自己做了一个测试demo,来研究Abort抛出异常的时机.废话少说,直接上代码: ...

  3. iOS学习之SKTagView的使用

    SKTagView是一款支持自动布局的标签tag. 特性: -流式展示标签 -可以配置标签的颜色.事件.间隔.外边距等 -支持Auto layout -可以在UITableViewCell中良好展示 ...

  4. maven 配置

    四.eclipse配置maven eclipse---window---maven------User Settings: 之前设置的仓库的位置: 五.idea15配置maven idea14---s ...

  5. simple_html_dom配合snoopy使用

    https://github.com/samacs/simple_html_dom Snoopy的特点是“大”和“全”,一个fetch什么都采到了,可以作为采集的第一步.接下来就需要用simple_h ...

  6. HDU 5113 dfs剪枝

    题意:告诉格子规格,颜色个数,以及每个颜色能涂得格子数目,问是否能够实现相邻两个格子的颜色数目不相同. 分析:因为数据很小,格子最多是5 * 5大小的,因此可以dfs.TLE了一次之后开始剪枝,31m ...

  7. 修改SharePoint 2013中item Created by 信息

    因为公司的系统有点小bug.额,要做点坏事,把系统没记上的东西偷偷补上去,但是item的created by变成了我(这怎么行,不能让别人知道我做了坏事,一定是隔壁小李干的! 懒得开visual st ...

  8. Java(一)

    这个学期要学习七章的东西,目前我对第一章的内容掌握的还算良好,我学会了布局管理器,菜单栏,面板等一些组件的使用,但是对于事件监听器的使用还是有所欠缺.另外对于其他六章的内容都一无所知,之前也没有浏览过 ...

  9. php策略模式

    一.编写一个简单的网页计算器功能 代码片段: 视图页面(两个输入框,一个下拉列表选择操作符,一个计算按钮) 后台php程序处理(最原始的写法) 假如新加一个运算方式(取余),那么就得修改php后台程序 ...

  10. 昨天晚上画了个带apple的图:ide插件与php和xdebug通信原理图,周末写1个调试器。

    昨天晚上画了个带apple的图:ide插件与php和xdebug通信原理图,周末写1个调试器.