在计算机系统中,另一种争用激烈的资源就是CPU。执行每个进程都需要访问CPU,但通常一些想要访问CPU的进程比系统上的CPU内核更活跃。因此操作系统必须在运行的进程之间共享CPU内核,保证每个进程都可以正常访问CPU,保证每个进程都可以正常访问CPU,从而顺利执行。
我们已经知道。进程之间是彼此独立运行的,都有各自的地址空间,以防止一个进程影响其他进程的行为,但在许多应用程序中,让两个独立执行的路径同时运行,而且不受每个路径必须在各自的地址空间中运行的限制,这样做非常有用。这一执行单元被称为线程,多个线程都执同一程序的代码,并在同一进程中运行(从而共享相同的地址空间),但却彼此独立。
对于操作系统来说,线程是调度的基本单元;当操作系统调度程序考虑接下来在CPU上调度什么时,只需要查看系统上的活动线程。进程如果执行,至少要包含一个线程;一个新进程开始运行时,操作系统会自动为其创建初始线程。
调度器有两个作用:防止CPU闲置,否则会浪费宝贵的硬件资源;让所有线程公平的访问CPU,防止因为单个线程独占CPU而导致其他线程无法执行。为此,一个线程会在可用的CPU内核上调度执行,直到一下任一事件发生:
1.过了一定事件后,那时原线程将被操作系统抢占,调度执行另一个线程。
2.线程在等待操作完成时,将不能执行,在这种情况下,调度器会让另一个线程在CPU上运行,从而阻塞原来的线程。这可以防止CPU在一个线程没有工作时处于闲置状态,从而最大限度的提法哦CPU在执行代码上花费的时间。线程还可以调用sleep()函数,自动放弃cpu时间,这会使当前线程的执行延迟一段时间。
为一个应用程序添加多个线程的一个原因是,可以使其在多个CPU内核中并发执行,从而能够将复杂的操作划分为多个同步运行的小步骤,从而加速应用程序的执行。即使在单核计算机上,多线程也有其优点。通过在活动线程之间快速切换,调度器会让用户产生所有线程同时运行的错觉。及时一个线程被阻塞或者进入了死循环,也不会影响其他线程的响应速度。这样,就可以将好事操作移到后台执行,让应用程序的其他部分自由地响应用户交互。
对于与硬件交互的应用程序,一种常见的设计是将访问硬件的代码放到自身的线程中。软件代码等待硬件响应时常常进入阻塞状态;通过移除主程序线程中的这些代码,当程序需要等待硬件响应时,就不会对其用户界面产生影响。
另一种使用线程常见的情况是,保证软件是以最先的延迟来响应硬件事件。应用程序在收到硬件通知之前可以创建一个阻塞线程,然后可以使用多种技术与该线程通信,线程被阻塞时,调度器不需要给他访问CPU的机会,因此线程的存在并不影响系统的性能。但在硬件发出时间通知之后,线程会变为非阻塞状态并在cpu上调度执行,并且可以自由执行任何需要的操作,从而响应硬件。