http://blog.csdn.net/oyi319/article/details/6851371

一、六种多线程方法

.NET Framework2.0框架提供了至少4种方式实现多线程,它们是“BackgroundWorker”组件、委托的异步调用、线程池ThreadPool以及线程类Thread;.NET Framework 4.0增加了任务并行库TPL和PLINQ技术,可利用Task和并行计算的方法实现。下面列举这6种方法。

1.      BackgroundWorker组件

命名空间:System.ComponentModel

程序集:System.dll

BackgroundWorker可以用于协助开发WinForm应用程序或WPF应用程序。它作为一个组件发布,提供工作线程的进度反馈、完成事件和取消工作线程方法。在Visual Studio设计器界面里,通过“工具箱”方便地将它加入设计界面,还能通过“属性”窗口设置它的属性和事件。

将工作代码写在DoWork事件处理程序里,在主线程(UI线程)调用BackgroundWorker对象的RunWorkerAsync方法即可在一个独立的线程里启动DoWork事件的处理程序。

  1. backgroundWorker1.RunWorkerAsync(new Parameters(PrimesFrom,PrimesTo));

若需要在UI上显示工作进度,先使BackgroundWorker对象的WorkerReportsProgress属性设置为True,然后在DoWork事件处理程序里直接用BackgroundWorker对象的ReportProgress方法向UI线程报告工作进度和进度信息,编写BackgroundWorker对象的ProgressChanged事件处理程序,来获得工作进度,更新UI上的进度条等。

通常我们还希望点击“取消”按钮,能取消后台工作任务。先使BackgroundWorker对象的WorkerSupportsCancellation属性设置为True,然后在“取消”按钮的单击事件处理程序里直接调用BackgroundWorker对象的CancelAsync方法;在DoWork事件处理程序里,通过BackgroundWorker对象的CancellationPending属性便可得知是否有请求取消操作。

  1. backgroundWorker1.CancelAsync();

当DoWork事件处理程序返回后,会在UI线程上产生RunWorkerCompleted事件。

2.      委托的异步调用

.NET Framework中的许多对象支持同步和异步两种调用方法,它们的异步调用方法名称如BeginXXX。委托也支持同步调用(Invoke)和异步调用(BeginInvoke)两种方式。异步调用是不阻塞当前线程,使委托的方法与调用方代码异步执行;也可以在后台线程里,通过调用支持异步方法的.NET Framework对象(如WinForm的Form对象和WPF的Dispatcher对象)委托的代码,使代码在这些对象所在的线程里执行——这个技巧在后台线程请求执行UI线程上的代码时非常有用。

  1. if (this.InvokeRequired) //Form1的多线程方法中的代码片段(WPF中也有类似的属性)
  2. {
  3. varupdate = new Action(TaskCompleted); //调用TaskCompleted方法更新UI
  4. this.BeginInvoke(update);
  5. }

在当前的类或者一个新类里编写一个后台执行代码的入口方法,然后在UI线程里声明指向此入口方法的委托对象,执行委托对象的BeginInvoke方法即可。委托对象的BeginInvoke方法的参数由两部分组成,第一部分是委托函数的参数,第二部分是委托方法异步调用完成后启动的方法和参数,可以不指定第二部分。

  1. varworker = new Action<Parameters>(FindPrimesViaDelegate); //委托
  2. worker.BeginInvoke(new Parameters(PrimesFrom,PrimesTo), TaskComplete, null);

从.NET Framework 3.5开始,支持9个传入参数的Action泛型委托和8个传入参数、1个返回值的Func泛型委托,到.NETFramework 4.0,支持传入参数达16个的Action和Func泛型委托。它们被定义在System命名空间,程序集mscorlib.dll,从.NET Framework 3.5时代后,较少的使用Delegate关键字自定义委托了。

3.      线程池ThreadPool

命名空间:System.Threading

程序集:mscorlib.dll

每个进程拥有一个线程池。托管代码的线程池的最多支持线程数目与.NET Framework版本及CPU数目等硬件环境有关。在.NET Framework 4.0中,默认每个可用的CPU处理器增加250个辅助线程和1000个I/O线程。可使用SetMaxThreads方法更改线程池的最多线程数(注:承载.NET Framework的非托管代码,如C++,可使用mscoree.h头文件的CorSetMaxThreads函数更改线程池大小)。除了SetMaxThreads方法,还可以使用GetMaxThreads、GetMinThreads、SetMinThreads方法获得或更改线程数。.NET
Framework中的许多多线程的类或组件(如System.Threading.Timer),就是在线程池中运行的。

需要记住一点的是,线程池线程都是后台线程,即线程池线程的IsBackground属性都为True,全部前台线程退出后,线程池线程将被强行中断。

用QueueUserWorkItem方法将一个无参数或者仅一个参数的void方法加入到线程池启动。

  1. ThreadPool.QueueUserWorkItem(FindPrimes, newParameters(PrimesFrom, PrimesTo)); //启动一个线程池线程

4.      线程Thread

命名空间:System.Threading

程序集:mscorlib.dll

将一个无参数或者仅一个参数的void方法委托给Thread实例,调用Thread对象的Start方法启动一个线程,可对它进行优先级、前后台线程、线程单元状态、线程状态及名称等更多细致的控制。

  1. var t= new Thread(FindPrimes)
  2. {
  3. Name = "FindPrimes",
  4. IsBackground = true
  5. };
  6. t.Start(new Parameters(PrimesFrom, PrimesTo)); //启动一个线程

5.      任务Task

命名空间:System.Threading.Tasks

程序集:mscorlib.dll

Task作为.NETFramework 4.0推崇的多线程代替办法,方便的控制任务的有序或并行执行,充分地发挥多核CPU性能,将多项任务平衡分配给每个可用的CPU。由于任务中的某些方法使用了数据共享锁技术,可使用Dispose方法显式地销毁这些资源。泛型版本的任务还能取得其返回结果,通常任务作为数组并发执行的。利用Windows任务管理器或性能监视器能监视程序的CPU利用率的波形图。

  1. _tokenSource= new CancellationTokenSource();//用于取消任务
  2. Task.Factory.StartNew(FindPrimesInTask,new Parameters(PrimesFrom,PrimesTo), _tokenSource.Token); //启动一个任务

6.      并行计算Parallel

命名空间:System.Threading.Tasks

程序集:mscorlib.dll

充分发挥CPU的多核性能,Parallel.Invoke方法可以同时运行多个并行任务,Parallel.For和Parallel.ForEach方法可以并行循环和迭代IEnumerable<T>集合。

  1. Parallel.For(parameters.Min, parameters.Max,
  2. (count, loop) =>
  3. {
  4. //并行执行的代码
  5. //注:在Parallel.For里,第一个参数是min至max的自变量,
  6. //第二个参数是指这个并行循环体参数,可执行中断并行循环体等控制方法
  7. }

二、取消线程

除了BackgroundWorker组件,在.NETFramework 4.0之前的多线程框架中是不提供类似CancellationToken类型用于支持多线程的取消请求的。常用的办法是轮询检查一个线程间共享的取消标记的方式,获得取消请求,甚至编写一个线程管理器来增强多线程的可控性。

.NET Framework4.0对多线程和并行运算进行了增强和改进,CancellationTokenSource对象可以在多线程方案中更有效的发出取消请求。

命名空间:System.Threading

程序集:mscorlib.dll

  1. //1.声明一个CancellationTokenSource对象
  2. private CancellationTokenSource _tokenSource;
  3. //2.实例化_tokenSource对象
  4. _tokenSource= new CancellationTokenSource();
  5. //3.在支持CancellationToken的代码里判断取消请求
  6. if(_tokenSource.IsCancellationRequested)
  7. {
  8. loop.Stop();
  9. }
  10. //4.在控制线程代码里用调用取消请求
  11. _tokenSource.Cancel();

三、演示

在Wrox出版社的《VisualBasic 2005高级编程》第22章有一个求10000内素数的例子讲解.NET的多线程技术,我写了类似的演示代码,演示6种多线程的方法在后台计算素数,而不阻塞UI。

值得一提的是,在任务并发计算素数的演示里,利用CPU多核计算,得到结果所花费的时间有显著的提高。上图中最后一个波峰可看到CPU 0和CPU 1的并行工作情况。

代码和演示程序,请下载附件。

多线程演示_Src.7z 20KB:http://m2nlight.ys168.com/

六种多线程方法解决UI线程堵塞的更多相关文章

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

           我们先来看一段运行时会抛出 InvalidOperationException 异常的代码段: private void btnThreadA_Click(object sender, ...

  2. c#多线程(UI线程,控件显示更新) Invoke和BeginInvoke 区别

    如果只是直接使用子线程访问UI控件,直接看内容三,如果想深入了解从内容一看起. 一.Control.Invoke和BeginInvoke方法的区别 先上总结: Control.Invoke 方法 (D ...

  3. [转] c#多线程(UI线程,控件显示更新) Invoke和BeginInvoke 区别

    如果只是直接使用子线程访问UI控件,直接看内容三,如果想深入了解从内容一看起. 一.Control.Invoke和BeginInvoke方法的区别 先上总结: Control.Invoke 方法 (D ...

  4. Winform非UI线程更新UI界面的各种方法小结

    我们知道只有UI线程才能更新UI界面,其他线程访问UI控件被认为是非法的.但是我们在进行异步操作时,经常需要将异步执行的进度报告给用户,让用户知道任务的进度,不至于让用户误认为程序“死掉了”,特别是对 ...

  5. 非UI线程更新UI界面的各种方法小结

    转载:https://www.cnblogs.com/xiashengwang/archive/2012/08/18/2645541.html 我们知道只有UI线程才能更新UI界面,其他线程访问UI控 ...

  6. WPF 非UI线程更新UI界面的各种方法小结

    转载:https://www.cnblogs.com/bdbw2012/articles/3777594.html 我们知道只有UI线程才能更新UI界面,其他线程访问UI控件被认为是非法的.但是我们在 ...

  7. C#.NET使用Task,await,async,异步执行控件耗时事件(event),不阻塞UI线程和不跨线程执行UI更新,以及其他方式比较

    使用Task,await,async,异步执行事件(event),不阻塞UI线程和不跨线程执行UI更新 使用Task,await,async 的异步模式 去执行事件(event) 解决不阻塞UI线程和 ...

  8. 学习通过Thread+Handler实现非UI线程更新UI组件

    [Android线程机制] 出于性能考虑,Android的UI操作并不是线程安全的,这就意味着如果有多个线程并发操作UI组件,可能导致线程安全问题.为了解决这个问题,Android制定了一条简单的规则 ...

  9. 学习通过Thread+Handler实现非UI线程更新UI组件(转)

    [Android线程机制] 出于性能考虑,Android的UI操作并不是线程安全的,这就意味着如果有多个线程并发操作UI组件,可能导致线程安全问题.为了解决这个问题,Android制定了一条简单的规则 ...

随机推荐

  1. Send Excerpts from Jenkins Console Output as Email Contents

    Sometimes we need to send some excerpts from Jenkins console output (job logs) as email, such as tes ...

  2. 安鸾CTF-cookies注入

    什么是cookie注入? cookie注入的原理是:修改cookie的值进行注入 cookie注入其原理也和平时的注入一样,只不过注入参数换成了cookie 例如:PHP $_REQUEST 变量变量 ...

  3. Redis如何实现分布式锁

    今天我们来聊一聊分布式锁的那些事. 相信大家对锁已经不陌生了,我们在多线程环境中,如果需要对同一个资源进行操作,为了避免数据不一致,我们需要在操作共享资源之前进行加锁操作.在计算机科学中,锁(lock ...

  4. pikachu 不安全的url跳转

    不安全的url跳转问题可能发生在一切执行了url地址跳转的地方.如果后端采用了前端传进来的(可能是用户传参,或者之前预埋在前端页面的url地址)参数作为了跳转的目的地,而又没有做判断的话就可能发生&q ...

  5. windows和liunx下换行符问题

    区别 windows换行符是: \r\n liunx换行符是: \n 问题 程序处理的时候就会有问题,因为在Windows的文件多了一个\r 解决办法(转换文件格式) vim file :set fi ...

  6. 程序员必须知道的数据结构:HashMap 与 LinkedHashMap

    为什么要说 HashMap 与 LinkedHashMap?第一:这两种数据结构是 Java Coder 中经常使用的数据结构.第二:这两种结构是最合适的能说明链表与数组的结构关系.在开始之前首先必须 ...

  7. spring boot , spring security 安全的认证

    pom 文件 ------------------------------------------------------------------- <dependencies> < ...

  8. 入门数据结构与算法,看这一个就够了,知识点+LeetCode实战演练

    本笔记来自拉钩教育300分钟搞定算法面试 算法与数据结构 要掌握一种数据结构,就必须要懂得分析它的优点和缺点. 在考虑是否应当采用一种数据结构去辅助你的算法时,请务必考虑它的优缺点,看看它的缺点是否会 ...

  9. Redis3.0.0集群一键脚本 -by古斌

    下载地址(以交由码云托管): https://gitee.com/gubin0412/Redis3.0.0 赋予脚本执行权限  chmod +x redis-gubin.sh 使用 ./redis-g ...

  10. Linux centos 安装 Node.js

    官网下载地址 https://nodejs.org/zh-cn/download/ 1.下载二进制文件 (x64)   相当于  https://nodejs.org/dist/v10.16.3/no ...