windows为什么要支持线程
线程开销
CPU的发展
使用线程的理由
如何写一个简单Parallel.For循环
数据并行
Parallel.For剖析
优秀软件的一个关键特征就是具有并发性。过去的几十年,我们可以进行并发编程,但是
难度很大。以前,并发性软件的编写、调试和维护都很难,这导致很多开发人员为图省事
放弃了并发编程。新版 .NET 中的程序库和语言特征,已经让并发编程变得简单多了。随
着 Visual Studio 2012 的发布,微软明显降低了并发编程的门槛。以前只有专家才能做并发
编程,而今天,每一个开发人员都能够(而且应该)接受并发编程。
许多个人电脑和工作站都有多核CPU,可以同时执行多个线程。为了充分利用硬件,您可以将代码并行化,以便跨多个处理器分发工作。
在过去,并行需要对线程和锁进行低级操作。Visual Studio和.NET框架通过提供运行时、类库类型和诊断工具来增强对并行编程的支持。这些特性是在.NET Framework 4中引入的,它们使得并行编程变得简单。您可以用自然的习惯用法编写高效、细粒度和可伸缩的并行代码,而无需直接处理线程或线程池。
下图展示了.NET框架中并行编程体系结构。
1 基本概念 1.1 并发编程
并发
同时做多件事情
这个解释直接表明了并发的作用。终端用户程序利用并发功能,在输入数据库的同时响应用户输入。服务器应用利用并发,在处理第一个请求的同时响应第二个请求。只要你希望程序同时做多件事情,你就需要并发。
多线程
并发的一种形式,它采用多个线程来执行程序。从字面上看,多线程就是使用多个线程。多线程是并发的一种形式,但不是唯一的形式。
并行处理
把正在执行的大量的任务分割成小块,分配给多个同时运行的线程。
为了让处理器的利用效率最大化,并行处理(或并行编程)采用多线程。当现代多核 CPU行大量任务时,若只用一个核执行所有任务,而其他核保持空闲,这显然是不合理的。
并行处理把任务分割成小块并分配给多个线程,让它们在不同的核上独立运行。并行处理是多线程的一种,而多线程是并发的一种。
异步编程
并发的一种形式,它采用 future 模式或回调(callback)机制,以避免产生不必要的线程。
一个 future(或 promise)类型代表一些即将完成的操作。在 .NET 中,新版 future 类型
有 Task 和 Task 。在老式异步编程 API 中,采用回调或事件(event),而不是
future。异步编程的核心理念是异步操作:启动了的操作将会在一段时间后完成。这个操作
正在执行时,不会阻塞原来的线程。启动了这个操作的线程,可以继续执行其他任务。当
操作完成时,会通知它的future,或者调用回调函数,以便让程序知道操作已经结束。
NOTE:通常情况下,一个并发程序要使用多种技术。大多数程序至少使用了多线程(通过线程池)和异步编程。要大胆地把各种并发编程形式进行混合和匹配,在程序的各个部分使用
合适的工具。
任务并行库(TPL)是System.Threading和System.Threading.Tasks命名空间中的一组公共类型和API。
TPL动态地扩展并发度,以最有效地使用所有可用的处理器。通过使用TPL,您可以最大限度地提高代码的性能,同时专注于您的代码的业务实现。
从.NET Framework 4开始,TPL是编写多线程和并行代码的首选方式。
2 线程基础 2.1 Windows 为什么要支持线程 在计算机的早期岁月,操作系统没提供线程的概念。事实上,整个系统只运行着一个执行线程(单线程),其中同时包含操作系统代码和应用程序代码。只用一个执行线程的问题在于,长时间运行的任务会阻止其他任务执行。
例如,在16位Windows的那些日子里,打印一个文档的应用程序很容易“冻结”整个机器,造成OS和其他应用程序停止响应。有的程序含有bug,会造成死循环。遇到这个问题,用户只好重启计算机。用户对此深恶痛绝。
于是微软下定决心设计一个新的OS,这个OS必须健壮,可靠,易于是伸缩以安全,同同时必须改进16位windows的许多不足。
微软设计这个OS内核时,他们决定在一个进程(Process)中运行应用程序的每个实例。进程不过是应用程序的一个实例要使用的资源的一个集合。每个进程都被赋予一个虚拟地址空间,确保一个进程使用的代码和数据无法由另一个进程访问。这就确保了应用程序实例的健壮性。由于应用程序破坏不了其他应用程序或者OS本身,所以用户的计算体验变得更好了。