【进阶修炼】——改善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 ...
随机推荐
- 在java项目中使用 Lombok 及可能问题
一.Maven项目使用步骤一般包含两步,1)引入依赖 2)特定的 IDE 引入对应的插件 1)在POM中引入依赖 <!-- https://mvnrepository.com/artifact/ ...
- flowable Service介绍
1 原始数据库表说明 ACT_RE_*: RE stands for repository. Tables with this prefix contain static information su ...
- SQL SERVER 的排序规则
有时候查询数据库的时候会发现(比如做重名检查的时候):数据库的查询时对大小写不敏感的,也就是 A 和 a 是一样的. 也就是说 select * from tabletest where name = ...
- Android app 全局异常统一处理
异常处理需求 Android app 出现 crash 时,会出现 "程序异常退出" 的提示并关闭,体验不好,另外主要是无法知道哪里出现的崩溃,需要知道哪里造成的异常,就需要一个全 ...
- Push UIViewController with different orientation to previous
转自:http://stackoverflow.com/questions/6695837/push-uiviewcontroller-with-different-orientation-to-pr ...
- spring cloud 路由Zuul的高可用
Zuul的高可用非常关键,因为外部请求到后端微服务的流量都会经过Zuul.故而在生产环境中,我们一般都需要部署高可用的Zuul以避免单点故障. 笔者分两种场景讨论Zuul的高可用. Zuul客户端也注 ...
- Getting started with new I/O (NIO)
https://www.ibm.com/developerworks/java/tutorials/j-nio/j-nio.html https://www.ibm.com/developerwork ...
- Spark SQL利器:cacheTable/uncacheTable【转】
转自:http://www.cnblogs.com/yurunmiao/p/4936583.html Spark相对于Hadoop MapReduce有一个很显著的特性就是“迭代计算”(作为一个Map ...
- 转:安装Chrome不成功,显示代码为“0xa0430721”的解决办法
转自:https://www.douban.com/note/475994923/ Step1: Open your Application Data Folder. For Windows XP: ...
- 【Ubuntu】任务管理器loadruner
linux1 准备工作 可以通过两种方法验证服务器上是否配置了rstatd守护程序: ①使用rup命令,它用于报告计算机的各种统计信息,其中就包括rstatd的配置信息.使用命令rup 10 ...