线程详细剖析(四)

摘自《C++多核编程》

6.3 设置线程属性

存在一些可用来确定线程上下文的关于线程的信息。这些信息用于重建线程的环境。令对等线程相互之间产生区别的是id、定义线程状态的寄存器组、优先级和它的栈。这些属性使得线程有了自己的身份。

POSIX线程库定义了线程属性对象(attribute objec),它封装了线程属性的一个子集。这些属性可以被线程的创建者访问和更改。下面是可以被更改的线程属性:

1)竞争范围

2)栈大小

3)栈地址

4)分离的状态

5)优先级

6)调度策略和参数

线程属性对象可以同一个或多个线程关联。属性对象时定义了一个或一组线程的行为的概要(profile)。一旦对象被创建并初始化,可以在对线程创建函数的调用中重复引用它。如果重复使用,便会创建一组有着相同属性的线程。所有使用该属性对象的线程继承所有的属性值。一旦使用线程属性对象创建了线程,多数属性就不能够在线程使用中改动。

范围属性描述了哪些线程同特定线程竞争资源。线程在两个竞争范围内争夺资源:

1)进程范围

2)系统范围

线程依照竞争范围和分配域(它被指派到的处理器集)来同其他线程竞争处理器的使用。有着进程竞争范围的线程同进程中其他线程竞争,而有着系统竞争范围的线程同系统分配的其他进程的线程竞争资源。有着系统范围的线程和系统中所有线程一起被排序和调度。

线程的栈大小和位置是在线程被创建时设置的。如果线程栈的大小和位置没有在创建期间指定,则系统会赋给它默认的大小和位置。默认大小同系统相关,是由进程所允许的线程的最大数目、进程地址空间的指定大小、系统资源使用的空间等决定的。线程的栈大小必须足够大,以满足任何函数调用、线程调用的进程外部代码(如库代码)、局部变量存储的需要。有着多个线程的进程应当有足以满足其他所有线程栈的栈段。分配给进程的地址空间限制了栈的大小,从而限制了每个线程栈的大小。线程栈地址对于访问有着不同属性的内存区域的应用程序,可能会很重要。当您指定栈的位置时,需要注意点是,线程要求多少空间以及确保该位置不会同其他对等线程的栈发生重叠。

分离的线程时那些已经从它们的创建者中分离出来的线程。它们在终止或退出时,不需要同其他对等线程或主线程进行同步。它们仍共享所属进程的地址空间,但是由于它们是分离的,创建它们的进程或线程不能对它们进行控制。当线程终止时,线程的id和状态由系统保存,默认情况下,一旦线程终止,该情况会通知给创建者,线程的id和状态会返回给创建者。如果线程是分离的,则不会使用资源来保存状态或线程id。这些资源立即可以被系统重用。如果线程的创建者不需要在继续处理之前等待线程终止,后者如果线程不要求在终止时同其他对等线程进行任何类型的异步,那么该线程可以成为一个分离的线程。

线程从进程继承调度策略。线程有优先级,而且优先级最高的线程会在较低优先级的线程之前执行。通过对线程区分优先次序,系统中需要立即执行或响应的任务会被指定到处理器上并得到时间片。如果有着更高优先级的线程可以运行,则正在执行的线程会被抢占。线程的优先级可以被降低或提高。调度策略也决定了哪个线程会被指派到处理器上。可使用的策略有先进先出、轮询等。通常,没有必要在进程执行期间改变线程的调度属性。如果进程环境发生变动,改变了时间约束,使得您需要改进进程的性能,则可能需要对调度进行改动。但是要考虑到改动应用程序中指定进程的调度属性,可能会对应用程序的总体性能产生负面影响。

 

6.4 线程的结构

我们已经讨论了进程以及线程同他所属进程的关系。图6-2显示了包含多个线程的进程结构。进程通过上下文和属性区别于系统中其他进程,线程也可以通过上下文和属性区别于其他对等线程。进程有代码段、数据段和栈段。线程同进程共享代码段和栈段。进程的栈通常从内存的高地址开始,向下增长。线程栈以下一个线程栈的开始位置为编辑。可以看到,线程栈包含其局部变量。进程的全局变量位于数据段中。threadA和threadB的上下文包括线程id、状态、优先级、处理器寄存器等。程序计数器指向代码段中函数task1() 和 task2()中下一条可执行指令。栈指针指向它们各自的栈的顶部。线程属性对象同一个线程或一组线程相关联。在本例中,两个线程使用相同的线程属性。

6.4.1 线程状态

线程是当前进程被调度执行时的执行单元。如果进程中只有一个线程,该线程是只拍到处理器内核的主线程。如果进程有着多个线程,而且对于该进程有多个处理器可用,那么所有的线程都会被指派到处理器上。

当线程被调度到处理器内核上执行时,它会改变自身的状态。线程状态是指在任意指定时间所处的模式或情形。线程有着同第5章进程接收的状态和转换相同的状态和转换。

有4种常见的状态:

1)可运行

2)运行(活动)

3)停止

4)休眠(阻塞)

存在如下的转换:

1)抢占

2) 接到信号

3)分派

4)时间片用完

主线程可以决定整个进程的状态。如果主线程时唯一的线程,则主线程的状态同进程的状态相同。如果主线程在休眠,进程也在休眠。如果主线程在运行,进程也在运行。对于有着多个线程的进程,只有进程中所有线程都处于休眠或停止状态时,我们才能够认为整个进程休眠或停止。另一方面,如果一个线程是活动的(可运行或运行),那么进程会被认为是活动的。

 

6.4.2 调度和线程竞争范围

线程有着两种竞争范围:

1)进程竞争

2)系统竞争

有着进程竞争范围的线程与相同进程的其他线程进行竞争。这些是混合线程(用户级和内核级线程),系统将创建内核级线程池,用户级线程将映射到它们。这些内核级线程是非绑定的,可以映射到一个线程或多个线程。然后内核根据调度属性将内核线程调度到处理器上。

有着系统竞争范围的线程同系统范围内进程的线程进行竞争。这个模型由每个内核级线程对应一个用户级线程组成。用户线程在线程的生命期内绑定到内核级线程上。内核线程单独负责在一个或多个处理器上调度线程执行。这个模型使用线程的调度属性,根据系统中所有线程来进行线程调度。线程的默认竞争范围根据实现定义。例如,对于Solaris10,默认竞争范围是进程,但是丢与Suse Linux 2.6.13,默认竞争范围时系统范围。事实上,对于Suse linux 2.36.13,根本不支持进程竞争范围。

图6-3显示了进程和系统两种线程竞争范围的区别。在有8个内核的多核环境中,有2个进程。进程A有4个线程,进程B有2个线程。进程A的4个线程中的3个线程竞争范围是进程范围,一个线程的竞争范围是系统范围。进程B的两个线程中,一个线程竞争范围是进程范围,一个线程的竞争范围是系统范围。进程A中有着进程范围的线程竞争内核0和1,进程B中有着进程范围的线程将使用内核2.进程A和B中系统范围的线程竞争内核4和5.有着进程范围的线程映射到线程池。进程A的线程池中有3个内核级线程,进程B的线程池中有两个内核级线程。

竞争范围可以对应用程序的性能产生潜在影响。进程调度模型潜在的为做出调度决策提供较低的开销。因为只需要对一个进程中的线程进行调度。

 

posted @ 2019-01-08 19:37  hbg-rohens  阅读(233)  评论(0编辑  收藏  举报