进程(Process)是计算机中运行程序的实体,而本质上程序只是一组计算机指令的集合。多个进程可与同一个程序相关,它们或顺序或平行执行。程序(指令集合)是如何被处理器执行的呢?简单来说,程序指令会被操作系统加载到内存,处理器逐条执行这些指令。程序在被加载到内存后表现为与之对应的内存映像和执行上下文。由于处理器无区别执行每个程序,那么多进程同时执行是怎么回事?早期计算机系统的硬件资源昂贵且稀缺,特别是处理器,如果让处理器简单的将一个进程执行完再执行下一个,这样的简单的轮流执行机制在处理等待用户操作的程序时,处理器会处于空闲状态直到接收到用户操作,在等待期间,其他进程则没有机会被执行。这会对处理器造成极大浪费。此时就需要一个更加复杂的执行机制:多个程序可以同时共存于内存队列(进程队列),系统为处理器分配时间单元(极短的时间段),在每个时间单元内,处理器执行一个进程;当时间单元结束或进程执行完毕,则执行进程队列中下一个进程;在处理器切换不同进程时,前一个没执行完的进程的执行状态(即进程的上下文)会保存到内存,当下次被处理器执行时以便还原到上次执行结束时的现场;队列中的进程,如此被循环被执行。这种机制,虽然在微观上进程还是顺序执行的,但由于处理器时间单元极短(时间单元的具体长度取决于操作系统和处理器。),却在宏观上表现为多个程序被同时执行。这样对于每个进程都获得了相对均等的执行机会。
可以将一个进程看作是处理器对独立、完整任务的一次执行。那么在对这样的一个任务的执行上,是不是还存在时间调度上的优化呢?答案是肯定的,就像一个工厂,从原材料到成品肯定不止顺序完成的,而是多条生产线(生产单元)或并行、或顺序生产完成,最简单的并行生产是,产品与外包装肯定应该是同时并行生产,最后是组装成品(产品的包装)。进程也可被划分成多个执行单元以便于被处理器或并行、或顺序的执行。这个执行单元就是线程。这样线程就取代了进程成为处理器时间分配的最小执行单元。
再回头看看进程与线程。进程简单说就是执行中的程序,并提供程序运行的各种资源,如虚拟的地址空间、数据、对象句柄集、基础优先级等等;线程是系统处理机调度的最基本单位,一个进程可以有多个线程同时执行。每个线程都维护自己异常处理程序、调度优先级和一组系统用于在调度该线程前保存线程上下文的结构。线程上下文包括为使线程在线程的宿主进程地址空间中无缝地继续执行所需的所有信息,包括线程的 CPU 寄存器组和堆栈。
.NET Framework 将操作系统进程进一步细分为由 System.AppDomain 表示的、称为应用程序域的轻量托管子进程。一个或多个托管线程(由 System.Threading.Thread 表示)可以在同一个托管进程中的一个或任意数目的应用程序域中运行。虽然每个应用程序域都是用单个线程启动的,但该应用程序域中的代码可以创建附加应用程序域和附加线程。其结果是托管线程可以在同一个非托管进程中的应用程序域之间自由移动;您可能只有一个线程在若干应用程序域之间移动。.NET提供了方便的“线程池”类(ThreadPool)以供开发使用,它提供具有默认优先级的后台线程集合,并对其进行动态调度管理,在进程内它是唯一的。
异步通常用于执行时间可能较长的任务,如打开大文件、连接远程计算机或查询数据库。异步操作在主应用程序线程以外的线程中执行。应用程序调用方法异步执行某个操作时,应用程序可在异步方法执行其任务时继续执行。 其实质依然是多线程。
.Net Framework有两种异步编程方式:1)基于IAsyncResult接口;2)基于事件。
使用IAsyncResult编程的异步方式,.NET Framework提供了比较完整的API。这些API均以Begin开头的方法开始异步执行、以End开头的方法等待异步执行结束。.Net Framework在文件、流、网络、消息队列等功能上提供有异步操作版本API,允许通过委托异步调用(BeginInvoke和EndInvoke方法对)任何方法。
无论以哪种方式异步,其异步执行的结果对于编程可能都很重要。.Net Framework也同样提供了多种方式等待异步执行结果。
关于多线程、异步及线程同步编程是.Net开发人员应该掌握的重要技术之一。它甚至影响我们对.net的一些高级应用的理解。会使用多线程和异步只是个开端,线程安全才是最重要的。