【进阶修炼】——改善C#程序质量(5)
71, 区分异步和多线程的应用场景。
计算机的很多硬件,如硬盘,光驱,声卡,网卡都有DMA(Direct Memory Access)功能,它可以不占用cpu的资源,而异步的提出恰恰就是基于这个的。而多线程是操作系统上的并行执行的代码,是会占用cpu资源的。所以关于这两种的使用场景建议是:1)对于I/0密集型操作使用异步。2)对于计算密集型操作使用多线程。
72, 在线程同步中使用信号量。
值类型是不能被锁定的,引用类型上的等待机制,分为锁定和信号同步。锁定通过lock关键字或Monitor来完成;信号同步用AutoResetEvent,ManualResetEvent,Semaphore,Mutex,其中Mutex可以在不同进程间同步。
73, 避免锁定不恰当的对象。
同步对象在需要同步的多个线程中是可见的同一个对象,不要锁定字符串,类型本身,实例本身。降低同步对象的可见性,尽量让它变为private的。
74, 警惕线程的IsBackground。
用Thread类创建的线程是前台线程,如果前台线程不全部退出,应用程序就关不掉。而ThreaadPool里的线程默认是后台的。所以一些关键性的操作才需要前台线程,否则应将IsBackground设为true。
75, 警惕线程不会立即启动。
这取决于操作系统的调度,我们不能假设它会立即启动。
76, 警惕线程的优先级。
Thread和ThreadPool创建的线程优先级都是Nomal的,我们一般没有必要去修改这个优先级,对于Window操作系统,优先级越高在调度时会得到优先执行。
77, 正确停止线程。
1)正如线程不能立即启动一样,线程的停止也不是想停就能停,他必须做完必要的工作后才能停止,以传统的Thread.Abort方法为例,调用这个方法后,如果代码在和非托管代码打交道,只有等到这部分代码结束后才能引发ThreadAbortException,即便是在CLR环境中ThreadAbortException也不会立即引发。2)要正确停止线程,不在于调用者采取的行为(Abort),而更多的依赖于线程能否主动响应调用者的停止请求。线程应该开放一个Cancled接口,工作线程以某种频率检测Cancled标识,如果发生改变,线程自己负责退出。.net提供了一个CancellationTokenSource用于协作取消。cts.Token.IsCancellationRequested判断是否有取消操作,cts.Cancel()发送取消请求,线程结束时的如需回调,可以用cts.Token.Register(() => {// 回调 })注册一个回调函数;
78, 应避免线程数量过多。
对于Socket类型的操作应该用异步,而不要开线程。
79, 使用ThreadPool或BackgroundWorker替代Thread。
ThreadPool线程池可以重复利用空闲的线程。BackgroundWorker可以支持进度,完成通知,取消功能,内部也是用的线程池。
80, 用Task来替代ThreadPool。
Task在线程池的基础上进行了优化,Task提供了ContinueWith方法来提供后续任务的执行。Task提供了IsCanceled,IsCompleted,IsFaulted这几个属性用于查询任务的执行结果。并且还支持取消功能。Task的Result属性用于查询函数的返回结果,如果执行过程被取消,调用这个属性会引发AggregateException异常,Task比ThreadPool功能丰富了很多,应尽量多用Task。
81, 使用Parallel简化同步状态下的Task使用。
Parallel是一个静态类,提供了For,Foreach,Invoke方法启动多个Task执行任务。
82, Parallel简化但不等同于Task的默认行为。
Parallel的方法调用后是阻塞的,尽管他内部的Task是并行计算的,除非这些Task都执行完了,Parallel后面的代码才会得到执行。
83, 小心Parallel的陷阱。
Parallel的For方法还提供了线程开始前和结束后执行的方法。但由于Task使用的线程池,比如4次循环可能使用了1个或2个线程,这会带来这两个方法执行次数的不确定性。另外,toExclusive参数指定值为5的时候,循环结束的位置是它的前一个值4,而不是5。
84, 使用PLINQ。
PLINQ即并发执行的LINQ,由于是并发,对于大数据量的查询,它的效率可能会更好。要使用PLINQ,只用调用数据源的扩展方法AsParallel方法,在ForEach查询中,结果就会得到并行计算,当然输出是无序的。如果要按有序输出,可以在数据源上调用AsOrderd方法,如:arr.AsParallel().AsOrdered()。注意在查询的ForAll方法中,输出始终是无序的。关于PLINQ的使用,应该进行充分的学习了解后再使用,否则可能会带来意想不到的bug。
85, Task中的异常处理。
调用任务的Wait,WaitAny,WaitAll方法,或查询Result属性,如果Task发生了异常,会生成一个AggregateException,我们通过循环查询它的InnerException就可以知道所有的异常。但是Wait等方法是会阻塞主线程的。Task产生的异常,如果在Task内部没有得到处理,也没有抛给主线程的话,那么只有垃圾回收的时候才会触发TaskScheduler.UnobservedTaskException事件,如果调用了UnobservedTaskException的e.SetObserved();方法,异常就不会再往上传递,否则还会进一步触发CurrentDomain_UnhandledException事件,程序终止。所以,Task内部的异常应该立即得到处理。简单一点的做法是Task内部逻辑对捕获的异常进行处理,比如写日志之类的。另一种是,在外部定义一个事件,Task内部出现异常时,通过这个事件通知给外部,在这里进行异常的后续操作,这种方法比较可取。
86, Parallel中的异常处理。
Parallel中的异常和Task中的异常不一样。它可以抛出给主线程。但是我们也可以在内部声明一个AggregateExcption,把所有的异常捕获后,打包到AggregateExcption里面,一起抛出,个人感觉意义不大。
87, 区分Winform和WPF的线程模型。
Winform中是通过ISynchronizeInvoke接口的InvokeRequire属性来判断控件是否处在其他线程中。Winform中的所有控件都继承了这个接口。WPF中是通过DispatcherObject提供的CheckAccess和VerifyAccess方法来判断,其中VerifyAccess判断不是同一个线程会抛出异常。
88, 并行并不总是速度最快。
因为并行会带来额外的线程开销,对于小的循环体,计算量不大,并行反而会更慢。
89, 在并行方法中谨慎使用锁。
并行中有多个线程,加锁会带来线程的切换,影响性能。可以适当考虑用Interlock对整型进行原子加减操作而避免线程切换的开销。如果整个代码都是需要加锁的,那么实际上就没有并行的意义,用同步的方式或许会更好。
【进阶修炼】——改善C#程序质量(5)的更多相关文章
- 【进阶修炼】——改善C#程序质量(1)
这是一个大纲形式的概要,以便自己可以花较少的时间反复阅读.在开发中,多加注意这些有用的建议,让自己成为一个更优秀的程序员.内容主要来自<编写高质量代码-改善C#程序的157个建议>(陆敏技 ...
- 【进阶修炼】——改善C#程序质量(10)
158,不要写冗余注释. 注释应该写代码没有表达的东西. 代码能够自我描述就不要加注释. 159,废弃的注释应该尽早删除. 废弃的注释由于年代太久远,已经和现在的代码逻辑不匹配了,这样的注释只会误导人 ...
- 【进阶修炼】——改善C#程序质量(9)
140,使用默认的访问修饰符. 如果不加访问修饰符,成员变量的默认是private的,类默认是internal的.为了明确访问的权限,我倒是建议都加上访问修饰符,这省不了多少代码. 141,不知道该不 ...
- 【进阶修炼】——改善C#程序质量(8)
122,以<Company>.<Component>作为命名空间. 如Microsoft.Windows.Design.也可以用域名作为空间,如www.microsoft.co ...
- 【进阶修炼】——改善C#程序质量(7)
113,声明变量时考虑最大值. Ushort的最大值是65535,用于不同的用途这个变量可能发生溢出,所以设计时应充分了解每个变量的最大值. 114,MD5不再安全. MD5多用于信息完整性的校验.R ...
- 【进阶修炼】——改善C#程序质量(6)
90,不应为抽象类指定public的构造函数. 抽象类即使指定了public的构造函数,也是不能实例化的,编译通不过.抽象类的构造函数应该设定为protected,它的作用应该是初始化自己的成员,以及 ...
- 【进阶修炼】——改善C#程序质量(4)
46, 显示释放资源,需要实现IDisposable接口. 最好按照微软建议的Dispose模式实现.实现了IDisposable接口后,在Using代码块中,垃圾会得到自动清理. 47, 即使提供了 ...
- 【进阶修炼】——改善C#程序质量(3)
32, 总是优先考虑泛型. 泛型代码有很好的重复利用性,和类型安全性. 33, 应尽量避免在泛型类中声明静态成员. 静态成员达不到共享的目的.List<int>和List<Strin ...
- 【进阶修炼】——改善C#程序质量(2)
16, 元素可变的情况下应避免用数组. 数组是定长的集合,可以考虑用ArrayList或List<T>集合.ArrayList元素是object类型,有装箱的开销,性能较低.另外Array ...
随机推荐
- FreeSWITCH在会议室中持续播放音频文件
最近遇到一个客户需求,希望在会议室建立起来后,自动播放一段指定的声音. 已知会议室命令,假设建立起一个会议室号码3000,很容易实现以下功能: 一.播放一个声音文件一次 conference 3000 ...
- Mac下命令行下载android源代码并构建apk过程
前提是java .sdk.ndk .cmake.gradle .gradlew都已经安装和配置好. 1.下载源码: git clone http://git-ma.xxxx.com.cn/xxxx/x ...
- oozie 常用命令
1.验证wokflow.xmloozie validate /appcom/apps/hduser0401/mbl_webtrends/workflow.xml 2.提交作业,作业进入PREP状态 o ...
- Netflix OSS、Spring Cloud还是Kubernetes? 都要吧!
Netflix OSS是由Netflix公司主持开发的一套代码框架和库,目的是解决上了规模之后的分布式系统可能出现的一些有趣问题.对于当今时代的Java开发者们来说,Netflix OSS简直就是在云 ...
- 全局获取 (Activity)Context,实现全局弹出 Dialog
为什么需要一个全局的 (Activity)Context 需求1:在进入 app 的时候,要求做版本检测,有新的版本的时候,弹出一个 AlertDialog,提示用户版本更新 需求2:从别的设备挤下来 ...
- Android Webview SSL 自签名安全校验解决方案
服务器证书校验主要针对 WebView 的安全问题. 在 app 中需要通过 WebView 访问 url,因为服务器采用的自签名证书,而不是 ca 认证,使用 WebView 加载 url 的时候会 ...
- vim快速指南
vi编辑器有3种模式:命令模式.输入模式.末行模式.掌握这三种模式十分重要: 命令模式:vi启动后默认进入的是命令模式,任何模式下,按[Esc]键都可以返回命令模式.输入模式:可输入字符,在底部显示“ ...
- Atitit vue.js 把ajax数据 绑定到form表单
Atitit vue.js 把ajax数据 绑定到form表单 1.1. 使用场景:主要应用在编辑与提交场合..1 1.2. 绑定数据到form控件,可以使用jquery,不过vue.js更加简单1 ...
- [Windows Azure] How to Deploy a Database to Windows Azure
How to Deploy a Database to Windows Azure There are several different ways you can move an on-premis ...
- (原创)拨开迷雾见月明-剖析asio中的proactor模式(一)
使用asio之前要先对它的设计思想有所了解,了解设计思想将有助于我们理解和应用asio.asio是基于proactor模式的,asio的proactor模式隐藏于大量的细节当中,要找到它的踪迹,往往有 ...