c# 中的多线程和异步
前言:
1、异步和多线程有区别吗?
答案:多线程可以说是实现异步的一种方法方法,两者的共同目的:使主线程保持对用户操作的实时响应,如点击、拖拽、输入字符等。使主程序看起来实时都保持着等待用户响应的状态,而后台却有若干件事情在自己干。
2、异步和多线程的两大类
按消耗资源所在地可分为两类:硬件异步类和CPU异步类。
硬件异步类大概有以下这几类。
应用程序范围 |
支持包含异步方法的 API |
Web 访问 |
|
处理文件 |
|
使用图像处理 |
|
WCF 编程 |
|
与套接字处理 |
硬件异步的特点:将需要在后台执行的操作甩给底层硬件去执行,不占用线程和CPU资源。
CPU常用的异步方式、方法
1、独立的线程—ThreadStart
一般情况下,要为不会阻止其他线程的相对较短的任务处理多个线程并且不需要对这些任务执行任何特定调度时,使用 ThreadPool 类是一种最简单的方式。 但是,有多个理由创建您自己的线程:
如果您需要使一个任务具有特定的优先级。
如果您具有可能会长时间运行(并因此阻止其他任务)的任务。
如果您需要将线程放置到单线程单元中(所有 ThreadPool 线程均处于多线程单元中)。
如果您需要与该线程关联的稳定标识。 例如,您应使用一个专用线程来中止该线程,将其挂起或按名称发现它。
如果您需要运行与用户界面交互的后台线程,.NET Framework 2.0 版提供了 BackgroundWorker 组件,该组件可以使用事件与用户界面线程的跨线程封送进行通信。
2、ThreadPool—ThreadPool.QueueUserWorkItem(M())
3、任务,Task系列--普通任务、关联的任务(Task<T>.ContinueWith(…))、父子任务、任务工厂(TaskTactory<TResult>)
4、Parallel静态类--- System.Threading.Tasks.Parallel.For(…) System.Threading.Tasks.Parallel.ForEach(…) Parallel.Invoke(() => Sort());
5、PLINQ
6、定时器
System.Threading.Timer 内部是采用线程池来实现的,在内部,当前线程池会为所有的Timer对象只开辟一个线程去执行注册到timer上的方法。这意味着即使申明再多的Timer并不能保证他们是并行执行的。
System.Windows.Forms.Timer 计数器触发时,windows价格一个计时消息(WM_TIMER)注入调用线程的消息队列,Timer对象消耗的资源全部由调研线程内部完成,计时器的方法不会由多个线程并发执行。
System.Windows.Threading.DispatcherTimer System.Windows.Forms.Timer在WPF当作的使用
System.Timers.Timer 微软早期的timer控件 建议不要使用
3、区分多线程构成的间隔式死循环和Timer构成的定时触发死循环
我们在使用多线程和Timer往往是希望构建一个不影响UI响应的死循环。注意Timer的特点是定期就会触发下一次方法的执行,不管上一次执行是否执行完毕。而采用一个多线程代码形式构造一个死循环+Thread.Sleep(int)的方式的意思是:在每次执行完毕后间隔固定的时间再执行下一次循环。
4、多线程、异步编程面临的最大问题:状态、结果跟踪(即数据同步问题)
一、实现一个可取消的多线程操作
1.1采用ThreadPool.QueueUserWorkItem+CancellationTokenSource实现
private void CancelllationToken_Click_1(object sender, RoutedEventArgs e) { ///定义一个为可取消资源标志 CancellationTokenSource cts = new CancellationTokenSource(); ///定义第二个为可取消资源标志 CancellationTokenSource cts1 = new CancellationTokenSource(); ///实现一个可取消操作的回调函数, ThreadPool.QueueUserWorkItem(o => Count(cts.Token, )); ///为可取消资源标志注册取消后的回调函数(无参,无返回值,匿名委托) cts.Token.Register(() => Console.WriteLine("Canceled 1")); ///为可取消资源标志注册取消后的回调函数(有参,无返回值,显式申明委托) cts.Token.Register(o => TestCancellationMethead(, ), true); cts1.Token.Register(() => Console.WriteLine("Canceled 2")); ///连接两个可取消资源标志 var LinkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cts1.Token); ///给连接后的可取消资源标志集注册回调函数(匿名委托),集合中任意一个可取消资源标志取消,都将触发该回调函数 LinkedCts.Token.Register(() => Console.WriteLine("linkedCts canceled")); Thread.Sleep(); cts.Cancel(); } void TestCancellationMethead(int x, int y) { Console.WriteLine(x + y); } /// <summary> /// 一个可取消操作的回调函数,函数的关键是传入一个CancellationToken对象 /// </summary> /// <param name="token"></param> /// <param name="countTo"></param> void Count(CancellationToken token, Int32 countTo) { Console.WriteLine("一个可取消的操作开始执行"); ; count < countTo; count++) { if (token.IsCancellationRequested) { Console.WriteLine("一个可取消的操作被取消" + count.ToString()); break; } Console.WriteLine(count.ToString()); } }
本实现方案的关键点在于39行token.IsCancellationRequested作为一个标志位对操作(具体到本例代码为循环)进行了终止break。
其实可以把CancellationTokenSource 理解为一个全局变量,只是实现这个全局的方法比较特殊,是通过将CancellationTokenSource 自己本身作为参数传递给可取消操作的委托函数(Count)来实现的,具体代码为9行对应34行。
其实这样的功能完全可以采用传统的、非常简洁的写法,无需申明CancellationTokenSource 这样的大对象,只需额外申明一个bool类型的全局变量即可,如果无需作出<对取消后进行状态跟踪>等复杂操作,采用传统写法才是获取最佳性能的首选。
同等效果的代码如下:
bool IsCancellationRequested = false; private void CancelllationToken_DIY_Click_1(object sender, RoutedEventArgs e) { ThreadPool.QueueUserWorkItem(o => Count()); Thread.Sleep(); IsCancellationRequested = true; } void Count(Int32 countTo) { Console.WriteLine("一个可取消的操作开始执行"); ; count < countTo; count++) { if (IsCancellationRequested) { Console.WriteLine("一个可取消的操作被取消" + count.ToString()); break; } Console.WriteLine(count.ToString()); } }
1.2采用Task来实现
/// <summary> /// 定义一个多参数的函数,供o =>来调用 /// </summary> /// <param name="countTo"></param> /// <param name="y"></param> void TeskMetheadMoreInPar(int countTo, int y) { ; count < countTo; count++) { if (IsCancellationRequested) { Console.WriteLine("一个可取消的操作被取消" + count.ToString()); break; } Console.WriteLine((y * count).ToString()); } } int SumTest(int x, int y) { x = y = x * y; return x * y; } int TeskMetheadCancellation(int countTo, int y, CancellationToken ct) { ; ; count < countTo; count++) { ct.ThrowIfCancellationRequested(); Console.WriteLine((y * count).ToString()); z += count; } return z; } int TeskMetheadCancellation(int countTo, int y) { ; ; count < countTo; count++) { Console.WriteLine((y * count).ToString()); z += count; } return z; } private void NormalTesk_Click(object sender, RoutedEventArgs e) { #region 一个普通任务 Task<, )); t.Start(); MessageBox.Show(t.Result.ToString()); #endregion #region 使用全局变量终止普通任务 , ), null).Start(); Thread.Sleep(); IsCancellationRequested = true; #endregion #region 支持终止、取消的任务 CancellationTokenSource cts = new CancellationTokenSource(); Task<, , cts.Token), cts.Token); cts.Token.Register(() => { try { MessageBox.Show(t1.Result.ToString()); } catch { MessageBox.Show("调用一个终止操作的结果,会诱发一个异常!"); } }); t1.Start(); Thread.Sleep(); cts.Cancel(); #endregion #region 两个衔接的任务 Task<, )); t2.Start(); Task t3 = t2.ContinueWith(task => MessageBox.Show(t2.Result.ToString()), TaskContinuationOptions.OnlyOnRanToCompletion); #endregion }
二、针对I/O底层硬件的异步
c# 中的多线程和异步的更多相关文章
- 从Nginx的Web请求处理机制中剖析多进程、多线程、异步IO
Nginx服务器web请求处理机制 从设计架构来说,Nginx服务器是与众不同的.不同之处一方面体现在它的模块化设计,另一方面,也是最重要的一方面,体现在它对客户端请求的处理机制上. Web服务器和客 ...
- 细说.NET中的多线程 (二 线程池)
上一章我们了解到,由于线程的创建,销毁都是需要耗费大量资源和时间的,开发者应该非常节约的使用线程资源.最好的办法是使用线程池,线程池能够避免当前进行中大量的线程导致操作系统不停的进行线程切换,当线程数 ...
- C#中的多线程 - 基础知识
原文:http://www.albahari.com/threading/ 文章来源:http://blog.gkarch.com/threading/part1.html 1简介及概念 C# 支持通 ...
- 重新想象 Windows 8 Store Apps (44) - 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换
[源码下载] 重新想象 Windows 8 Store Apps (44) - 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换 作者:webabcd 介绍 ...
- 重新想象 Windows 8 Store Apps (45) - 多线程之异步编程: IAsyncAction, IAsyncOperation, IAsyncActionWithProgress, IAsyncOperationWithProgress
[源码下载] 重新想象 Windows 8 Store Apps (45) - 多线程之异步编程: IAsyncAction, IAsyncOperation, IAsyncActionWithPro ...
- Android 多线程----AsyncTask异步任务详解
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/3 ...
- [转载]ArcGIS Engine 中的多线程使用
ArcGIS Engine 中的多线程使用 原文链接 http://anshien.blog.163.com/blog/static/169966308201082441114173/ 一直都想写 ...
- iOS中实现多线程的技术方案
pthread 实现多线程操作 代码实现: void * run(void *param) { for (NSInteger i = 0; i < 1000; i++) { ...
- 第一章 管理程序流(In .net4.5) 之 实现多线程和异步处理
1. 概述 本章主要讲解.net4.5如何实现多线程和异步处理的相关内容. 2. 主要内容 2.1 理解线程 ① 使用Thread类 public static class Program { ...
随机推荐
- supervisor很赞!
最近,公司进行了新的架构设计,原来一个区服一组进程,变成了对外只有一台服,后面N组多进程进行服务的模式.于是,管理进程就变成了一个头痛的问题.原来是在写代码的目录里放置各种脚本解决的,关闭脚本,开启脚 ...
- 用Python对excel文件的简单操作
#-*-coding:utf8-*- import xlrd #代开excel文件读取数据 data = xlrd.open_workbook("C:\\Users\\hyl\\Deskto ...
- 2015GitWebRTC编译实录4
2015.07.17 libg711 编译通过[422/1600 ] CC obj /webrtc/modules/audio_coding/codecs/g711/g711.g711.o[423/1 ...
- C++ Primer : 第十一章 : 关联容器之概述、有序关联容器关键字要求和pair类型
标准库定义了两种主要的关联容器:map和set map中的元素时一些关键字-值(key-value)对,关键字起到索引的作用,值则表示与索引相关的数据.set中每个元素只包含一个关键字,可以完成高效的 ...
- ubuntu14.04 ibus pinyin wrong (ibus拼音乱拼问题)
在ubuntu14.04版的中文输入法ibus中,有时会出现拼音乱拼的问题.不过已经有了成熟的解决方案. 具体方法如下: apt-get remove ibus-pinyin apt-get inst ...
- 工作中遇到的问题--使用DTO减少数据字段
Location中包含如下字段以及AMfgObject中关于创建信息的字段,然而有时使用并不需要传输那么多数据,则对其中字段进行过滤. @Entity@Table(name = "LOCAT ...
- MySQL日志功能
1.查询日志 log={ON|OFF}:是否记录所有语句的日志信息于一般查询日志文件(general_log); log_output={TABLE|FILE|NONE},TABLE和FILE可以同时 ...
- Ansible安装配置Nginx
一.思路 现在一台机器上编译安装好nginx.打包,然后在用ansible去下发 cd /etc/ansible 进入ansible配置文件目录 mkdir roles/{common,install ...
- HDU 5513 Efficient Tree
HDU 5513 Efficient Tree 题意 给一个\(N \times M(N \le 800, M \le 7)\)矩形. 已知每个点\((i-1, j)\)和\((i,j-1)\)连边的 ...
- JAVA常用运算符
Java 语言中常用的运算符可分为如下几种: 1.算术运算符 int i = 5; int j = i++; // i = 6 j = 5 int j = ++i; // i = 6 j = 6 PS ...