您的位置:首页 > 其它

线程

2016-04-14 13:31 204 查看
线程的使用:

原因:

1)在许多应用中同时存在着多种活动。其中某些活动随着时间的推移会被阻塞。通过将这些程序分解成可以并行运行的多个顺序线程,程序设计模型会变得更简单。

2)由于线程比进程更轻量级,所以它们比进程更加容易创建,也更加容易撤销。

3)若多个线程都是CPU密集型的,则性能并不能获得增强。但如果存在着大量的计算和I/O处理,拥有多个线程允许这些活动彼此重叠进行,从而会加快应用程序的执行速度。

有限状态机:

每个计算机都有一个被保存的状态,存在一个会发生且使得相关状态发生改变的事件集合。

经典的线程模型:

进程的模型基于两个独立的概念:资源分组处理与执行。

线程则是将其分开!

线程中有一个程序计数器,用来记录接下来要执行哪一条指令。

线程拥有寄存器,用来保存线程当前的工作变量。

线程还拥有一个堆栈,用来记录执行历史。其中,每一帧保存了一个已调用的但是还没有从中返回的过程。

因次,进程用于把资源集中到一起,而线程则是在CPU上被调度执行的实体。

POSIX线程:

POSIX线程是线程的POSIX标准,定义了创建和操纵线程的一套API。

在用户空间中实现线程:

把整个线程包放在用户空间中,内核对线程包一无所知。从内核角度考虑,就是按整常的方式管理,即单线程进程。

在用户空间管理线程时,每个进程需要有其专用的线程表,用来跟踪该进程中的线程。仅仅记录各个线程的属性,如每个线程的计数器、堆栈指针、寄存器和状态。该线程表由运行时系统管理。

当某个线程做了一些会引起本地阻塞的事情后,它调用一个运行时系统的过程,这个过程检查该线程是否必须进入阻塞状态。如果是,它在线程表中保存该线程的寄存器,查看表中的可运行的就绪线程,并把新的线程的保存值重新装入寄存器中,只要堆栈指针和程序计数器一被切换,新的线程就又自动投入运行。

保存该线程状态的过程都是本地的,不需要陷入,不需要上下文切换,也不需要对内存高速缓存进行刷新。

问题:

在UNIX系统中,有一个select可以允许调用者通知预期的read是否会阻塞。首先进行select调用,然后只有在安全的情形下(即不会发生阻塞)才进行read调用。如果read调用阻塞,有关的调用就不会进行。

页面故障:

如果某个程序调用或跳转到一条不在内存中的指令上,就会发生页面故障,而操作系统将到磁盘上取回这个丢掉的指令(和该指令的”邻居们“)。

在单独的线程中没有时钟中断,所以不能用轮转调度(轮流)的方式调度程序。

在内核空间中实现线程:

不需要运行时系统,每个进程中也没有线程表。相反,在内核中有用来记录系统中所有线程的线程表。当某个线程希望创建一个新线程后者撤销一个已有的线程时,它进行一个系统调用,这个系统调用通过对线程表的更新完成线程创建或撤销工作。

所有能够阻塞线程的调用都以系统调用的形似实现,这与运行时过程相比,代价是相当可观的。

内核线程不需要任何新的,非阻塞系统调用。

混合实现:

使用内核级线程,然后将用户级线程与某些或者全部内核线程多路复用起来。

调度程序激活机制:

目标: 模拟内核线程的功能,但是为线程包提供通常在用户空间中才能实现的更好的性能和更大的灵活性。

基本思想是: 当内核了解到一个线程被阻塞后,内核通知该进程的运行时系统,并且在堆栈中以参数形式传递有问题的线程编号和所发生事件的一个描述。 内核通过在一个已知的起始地址启动运行时系统,从而发出了通知,这是对UNIX中信号的一种粗略模拟。

一旦如此激活,运行时系统就会重新调度其线程: 把当前线程标记为阻塞并从就虚表中取出另一个线程,设置其寄存器,然后启动之。稍后,当内核知道了原来的线程后又可以运行时,内核就又一次上行调用运行时系统,通知它这一事件。此时,运行时系统按照自己的判断,或者立即重启被阻塞的线程,或者把它放入就绪表中稍后运行。

弹出式线程:

使单线程代码多线程化:

单线程改成多线程出现的问题:

1)对线程而言是全局变量,并不是对整个程序也是全局的。

2)有许多库过程并不是可重入的。

3)堆栈的管理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线程 设计