摘要:
建议90:不要为抽象类提供公开的构造方法 首先,抽象类可以有构造方法。即使没有为抽象类指定构造方法,编译器也会为我们生成一个默认的protected的构造方法。下面是一个标准的最简单的抽象类: 其次,抽象类的方法不应该是public或internal的。抽象类设计的本意是让子类继承,而不是用于生成实 阅读全文
摘要:
建议89:在并行方法体中谨慎使用锁除了建议88所提到的场合,要谨慎使用并行的情况还包括:某些本身就需要同步运行的场合,或者需要较长时间锁定共享资源的场合。在对整型数据进行同步操作时,可以使用静态类Interlocked的Add方法,这就极大地避免了由于进行原子操作长时间锁定某个共享资源所带来的同步性 阅读全文
摘要:
建议88:并行并不总是速度更快并行所带来的后台任务及任务的管理,都会带来一定的开销,如果一项工作本来就能很快完成,或者说循环体很小,那么并行的速度也许会比非并行要慢。看这样一个例子,我们比较在同步和并行状态下的时间消耗: 以上代码在笔者当前的双核PC机上的输出为:同步耗时:00:00:00.0005 阅读全文
摘要:
建议87:区分WPF和WinForm的线程模型WPF和WinForm窗体应用程序都有一个要求,那就是UI元素(如Button、TextBox等)必须由创建它的那个线程进行更新。WinForm在这方面的限制并不是很严格,所以像下面这样的代码,在WinForm中大部分情况下还能运行(本建议后面会详细解释 阅读全文
摘要:
建议86:Parallel中的异常处理建议85阐述了如何处理Task中的异常。由于Task的Start方法是异步启动的,所以我们需要额外的技术来完成异常处理。Parallel相对来说就要简单很多,因为Parallel的调用者线程会等到所有的任务全部完成后,再继续自己的工作。简单来说,它具有同步的特性 阅读全文
摘要:
建议85:Task中的异常处理在任何时候,异常处理都是非常重要的一个环节。多线程与并行编程中尤其是这样。如果不处理这些后台任务中的异常,应用程序将会莫名其妙的退出。处理那些不是主线程(如果是窗体程序,那就是UI主线程)产生的异常,最终的办法都是将其包装到主线程上。在任务并行库中,如果对任务运行Wai 阅读全文
摘要:
建议84:使用PLINQLINQ最基本的功能就是对集合进行遍历查询,并在此基础上对元素进行操作。仔细推敲会发现,并行编程简直就是专门为这一类应用准备的。因此,微软专门为LINQ拓展了一个类ParallelEnumerable(该类型也在命名空间System.Linq中),它所提供的扩展方法会让LIN 阅读全文
摘要:
建议83:小心Parallel中的陷阱Parallel的For和ForEach方法还支持一些相对复杂的应用。在这些应用中,它允许我们在每个任务启动时执行一些初始化操作,在每个任务结束后,又执行一些后续工作,同时,还允许我们监视任务的状态。但是,记住上面这句话“允许我们监视任务的状态”是错误的:应该把 阅读全文
摘要:
建议82:Parallel简化但不等同于Task默认行为建议81说到了Parallel的使用方法,不知道大家是否注意到文中使用的字眼:在同步状态下简化了Task的使用。也就是说,在运行Parallel中的For、ForEach方法时,调用者线程(在示例中就是主线程)是被阻滞的。Parallel虽然将 阅读全文
摘要:
建议81:使用Parallel简化同步状态下Task的使用在命名空间System.Threading.Tasks中,有一个静态类Parallel简化了在同步状态下的Task的操作。Parallel主要提供3个有用的方法:For、ForEach、Invoke。For方法主要用于处理针对数组元素的并行操 阅读全文
摘要:
建议80:用Task代替ThreadPool ThreadPool相对于Thread来说具有很多优势,但是ThreadPool在使用上却存在一定的不方便。比如:ThreadPool不支持线程的取消、完成、失败通知等交互性操作。ThreadPool不支持线程执行的先后次序。以往,如果开发者要实现上述功 阅读全文
摘要:
建议79:使用ThreadPool或BackgroundWorker代替Thread使用线程能极大地提升用户体验度,但是作为开发者应该注意到,线程的开销是很大的。线程的空间开销来自:1)线程内核对象(Thread Kernel Object)。每个线程都会创建一个这样的对象,它主要包含线程上下文信息 阅读全文
摘要:
建议78:应避免线程数量过多在多数情况下,创建过多的线程意味着应用程序的架构设计可能存在着缺陷。经常有人会问,一个应用程序中到底含有多少线程才是合理的。现在我们找一台PC机,打开Windows的任务管理器,看看操作系统中正在运行的程序有多少个线程。在笔者当前的PC机上,线程数最多的一个应用程序是某款 阅读全文
摘要:
建议77: 正确停止线程开发者总尝试对自己的代码有更多的控制。例如,“让那个还在工作的线程马上停止下来”。然而,并非我们想怎样就可以怎样的,这至少涉及两个问题。第一个问题 正如线程不能立即启动一样,线程也并不是说停就停的。无论采用何种方式通知工作线程需要停止,工作线程都会忙完手头最紧要的活,然后在它 阅读全文
摘要:
建议76: 警惕线程的优先级线程在C#中有5个优先级:Highest、AboveNormal、Normal、BelowNormal和Lowest。讲到线程的优先级,就会涉及线程的调度。Windows系统是一个基于优先级的抢占式调度系统。在系统中,如果有一个线程的优先级较高,并且它正好处在就绪状态,系 阅读全文
摘要:
建议75:警惕线程不会立即启动现代的大多数操作系统都不是一个实时的操作系统,Windows系统也是如此。所以,不能奢望我们的线程能够立即启动。Windows内部会实现特殊的算法以进行线程之间的调度,在某个具体的时刻,它会决定当前应该运行哪个线程。这反映到最底层就是某个线程分配到了一定的CPU时间,可 阅读全文
摘要:
建议74:警惕线程的IsBackground在CLR中,线程分为前台线程和后台线程,即每个线程都有一个IsBackground属性。两者在表现形式上的唯一区别是:如果前台线程不退出,应用程序的进程就会一直存在,必须所有的前台线程全部退出,应用程序才算退出。而后台进程则没有这方面的限制,如果应用程序退 阅读全文
摘要:
建议73:避免锁定不恰当的同步对象在C#中,让线程同步的另一种编码方式就是使用线程锁。线程锁的原理,就是锁住一个资源,使得应用程序在此刻只有一个线程访问该资源。通俗地讲,就是让多线程变成单线程。在C#中,可以将被锁定的资源理解成new出来的普通CLR对象。既然需要锁定的资源就是C#中的一个对象,我们 阅读全文
摘要:
建议72:在线程同步中使用信号量所谓线程同步,就是多个线程在某个对象上执行等待(也可理解为锁定该对象),直到该对象被解除锁定。C#中对象的类型分为引用类型和值类型。CLR在这两种类型上的等待是不一样的。我们可以简单地理解为在CLR中,值类型是不能被锁定的,即不能在一个值类型对象上执行等待。而在引用类 阅读全文
摘要:
建议71:区分异步和多线程应用场景初学者有时候会将异步和多线程混为一谈。如果对它们之间的区别不是很清楚,很容易写出下面这样的代码: 上面的代码模拟了在一个Winform窗体程序中,单击Button获取某个网页的内容并显示出来。可以预见,如果该网页的内容很多,或者当前的网络状况不太好,获取网页的过程会 阅读全文